[問題] 請問物件在multithread下的生成與控管

作者: Keitaro (動き出す時間...)   2014-08-22 03:53:18
開發平台(Platform): (Ex: VC++, GCC, Linux, ...)
VC2008 MFC
問題(Question):
請教物件在multi-thread的情況下,物件的生成、控管、以及刪除的時機
補充說明(Supplement):
不好意思,不才小弟又上來請教一下各位了。
我想跟版上的各位高手請教一下,在multi-thread下,
物件生成、控管、以及刪除的時機與原則。
小弟工作上接手的project是MFC。
以往我的觀念是,一個母物件底下有什麼子物件,就在母物件底下生成(new)。
要刪除子物件,就在母物件的解構裡面執行(delete)。
子物件裡面如果還有包新的物件,照以上原則處理。
但我接手的這個proj,以前同事開發的寫法不是這樣。
當MainFrame建立起來後,就一次把所有的dialog全部建立起來。
比方說這樣的架構
MainFrame - Sub-DialogA - Sub-DialogA1
\ Sub-DialogA2
\
Sub-DialogB - Sub-DialogB1
在MainFrame的OnCreate把5個dialog全部new起來,
然後分別把Dialog A1/A2的指標傳給DialogA的指標,
DialogB1的指標傳給DialogB的指標。
刪除,當然全部在MainFrame的解構裡面做。
當然這樣也不是不可以,好處是把所有物件統一生成,統一刪除在同一處。
只是我不太喜歡,從屬關係要追一下程式碼。
追code不方便,除了統一生成統一刪除以外我不知還有什麼好處。
最近開始學習使用Multithread,碰上了一個問題。
我要寫一個自動下載的功能,架構如下
Create Create Create
MainFrame -> DialogA -> _MonitorThread -> _DownloadThread
(push queue) (pop queue)
1. MainFrame開啟timer自動push queue
2. DialogA在MainFrame底下生成
3. DialogA生成後開啟_MonitorThread
4. _MonitorThread監控queue,如果沒有東西下載(_DownloadThread指標為null),queue
裡面有東西,pop第一個執行_DownloadThread
5. 下載進度,_DownloadThread會顯示在DialogA中
5. 當_DownloadThread執行尚未結束前,_MonitorThread持續等待(WaitForSingleObject)
出現的問題是,如果user直接把UI整個關掉,MainFrame跑了OnDestroy,
在解構開始執行之前,我要等這兩個thread先結束,在abort flag丟給thread後,
我在MainFrame::OnDestroy裡面寫WaitForSingleObject(_MonitorThread)
測試結果發現常常會關不掉,檢查後發現我這樣的寫法會有dead lock。
問題出在於,_DownloadThread正好要把DialogA的顯示文字作更新的動作,
DialogA在MainFrame底下生成,可是此時MainFrame執行WaitForSingleObject,
所以無法回應,形成死結。
找到原因後,我想我的DialogA不能在MainFrame裡面建立,要在thread裡面建立才行。
create create create create
MainFrame -> _TestThread -> DialogA -> _MonitorThread -> _DownloadThread
在MainFrame裡面直接建一個新的_TestThread,只做一件事,把DialogA給建立起來。
//create thread
//CWinThread* pTestThread; 寫在MainFrame的header裡面
pTestThrtead = AfxBeginThread(CreateDlgFun, this);
//_TestThread
UINT CMainThread::CreateDlgFun(LPVOID pParam)
{
CMainFrame* pMF = (CMainFrame*)pParam;
CDialogA* pA = new CDialogA();
...
...
}
//MainFrame OnDestroy
void CMainFrame::OnDestroy()
{
...... // WaitForSingleObject(_TestThread)
}
我對於DialogA的建立,產生疑問:
1. CDialogA* pA = new CDialogA();
如果pA為local變數,那我就必須要讓Thread的完成下載動作、或user取消之前,
不能結束,否則pA就消失了。
2. 如果pA被宣告在MainFrame的header裡面
// in CMainFrame header
class CMainFrame
{
CDialog* pA;
...
}
//_TestThread
UINT CMainThread::CreateDlgFun(LPVOID pParam)
{
CMainFrame* pMF = (CMainFrame*)pParam;
if (pA != NULL)
pA = new CDialogA();
...
...
}
改成這樣的寫法,pA儲存在物件中,生命週期跟物件相同,我要對pA做操作會比較容易。
但這樣一來,會不會產生原來的問題?
雖然DialogA在Thread裡面new出來,但是pA還是放在MainFrame物件中。
這樣當_DownloadThread要求DialogA變更顯示文字時,會不會dead lock?
我的想法是,Dialog是thread建的,應該是thread掌管,會不會回應是thread控制,
pA只是記憶體的儲存位置,應該沒關係吧?
如果是的話,那麼pA new的時候需不需要Lock起來?
再來,刪除的時機為何?
1. 如果pA是local變數,那麼只要在thread結束前執行delete就好,沒問題。
2. 如果pA儲存在MainFrame物件呢?
我想應該還是要在thread結束前刪除吧?如果留給MainFrame去刪除,
proj越來越大、thread越多,應該會產生一堆問題。
請教版上各位高手,在multithread的物件管理的原則?
觀念可能很基本,但我這菜鳥覺得如果寫multithread觀念沒建好,管理出問題,
恐怕會有解不完的bug。
另外還有個問題順便問一下。
我知道thread function要被宣告成static。
class裡面的任何東西一旦被宣告成static,
class宣告的所有物件會共用被宣告為static的元件。
但我不解的是,那麼,thread的static function,
如果不放在物件裡面,變成global function,有什麼差別呢?
1. UINT static CMainFrame::_TestThread()
2. UINT static _TestThread()
請問有什麼差別?
還請版上各位高手指教,感激不盡!
作者: Killercat (殺人貓™)   2014-08-22 12:45:00
這優點就是pop dialog很快 因為早就生好了缺點就是會略為拖到啟動時間 不過這個我認為不嚴重
作者: TeaEEE (愛不趴 不愛趴)   2014-08-22 19:26:00
要避免在thread中生成UI物件,因為MS不保証物件生成是threThreadsafe
作者: EdisonX (卡卡獸)   2014-08-24 00:53:00
在很多 setting dialog 很多的時候 , 這方法不就... orz
作者: tyc5116 (累人啊....)   2014-08-26 09:22:00
同意3F,一個大原則,Thread只用來做數值運算,邏輯上的處理UI的部份都避免掉你的"5. 下載進度",我會把更新的數值放某變數內然後Dialog開個Timer,作為更新介面上的資訊如此,Thread可以完全避免掉UI的部份

Links booklink

Contact Us: admin [ a t ] ucptt.com