Wednesday, March 25, 2009

簡單的 MFC 多執行緒寫法

一般程式的執行過程是一個指令一個指令逐步動作,當前一步指令尚未結束前,下一個指令就不會執行,這樣的操作是屬於單執行緒程式。
所謂多執行緒,則是程式在主要的執行緒之外,新增其他執行緒工作,使2個(或多個)執行緒同時進行工作,也稱為平行處理程式。
我的功力只算剛入門,充其量就是剛寫出一個有子執行緒的小程式,這裡做個筆記。

在 VC++ 所建立的 MFC 專案內,假設要加入子執行緒的地方在 testview.cpp 內 (類別為 CTestView,繼承自 CView)
如果要開啟子執行緒的函式為 CTestView::Execute() ,則:
#include "testview.h"

UINT ChildThread_Function ( LPVOID param )
{
CTestView* view = (CTestView*) param;
view->DoSomething();
}

CTestView::DoSomething()
{
....
}

CTestView::Execute()
{
AfxBeginThread( ChildThread_Function, (LPVOID) this,  THREAD_PRIORITY_ABOVE_NORMAL);
}
ChildThread_Function 是全域函式,函式名稱由使用者自訂
AfxBeginThread 是MFC內呼叫子執行緒的方法,第一個參數是要執行的全域函式名稱(ChildThread_Function),第2個參數則是該全域函式的傳入值,此處傳入的是 CTestView 的指標。ChildThread_Function 這個子執行緒則是會呼叫 CTestView 的成員函式 DoSomething() 執行工作。

如果 ChildThread_Function 是 CTestView 的類別成員,則必須宣告為 static 才可以︰
class CTestView
{
..
..
static UINT ChildThread_Function ( LPVOID param );
};

UINT CTestView::ChildThread_Function ( LPVOID param )
{
..
..
}

14 comments:

Anonymous said...

你好,看到你之前在程設版發文.
是否可以請教依下關於mfc中AfxBeginThread的用法
我的程式跟上面的有點類似,也是用mfc Dialog based
我參考了上述的寫法,程式碼如下
// create a thread
AfxBeginThread(&CMy107Dlg::WorkerThreadProc,(LPVOID)this);

UINT CMy107Dlg::WorkerThreadProc( LPVOID Param )
{
CMy107Dlg *pDlg=(CMy107Dlg*)Param;

return 1;
}


但編譯器卻發生這樣的錯誤訊息
rror C2665: 'AfxBeginThread' : none of the 2 overloads can convert parameter 1
from type 'unsigned int (__thiscall CMy107Dlg::*)(void *)'

想請問一下我的作法有什麼問題
打擾了!謝謝

Chinson said...

將 UINT CMy107Dlg::WorkerThreadProc( LPVOID Param ) 在宣告時設成 static function 應該就可以了

ex.
class CMy107Dlg
{
.
.
static UINT WorkerThreadProc( LPVOID Param );
};

邵馬谷慕 said...

您好,不好意思,請教一下,我用您的方式成功建立了theard ,但是當我在
ThreadFunc裡呼叫get_status()函式出現了

error C2227 : left of'->get_status' must point to class/struct/union/generic type

的錯誤訊息,請問是那裡出了問題 ?

以下是我的code

UINT DCashdrawer::ThreadFunc (LPVOID pParam)
{
DCashdrawer* ptp = (DCashdrawer*) pParam;

pParam->get_status();

return TRUE;
}

BOOL DCashdrawer::th_start(void)
{
AfxBeginThread (&DCashdrawer::ThreadFunc, (LPVOID)this);

return TRUE;
}

class DCashdrawer : public CDialog
{
private:

BOOL th_start(void);
BOOL get_status(void);

static UINT ThreadFunc (LPVOID pParam);

:
:
}

BOOL DCashdrawer::get_status(void)
{
:
:
return TRUE;
}

邵馬谷慕 said...
This comment has been removed by the author.
Chinson said...

To 邵馬谷慕 :

是語法錯誤
應該改成︰
ptp->get_status();

邵馬谷慕 said...

您好,感謝您,問題已經解決了,另外再請教您一個問題

我在ThreadFunc裡寫了Setimer 的function , 每0.5秒會去讀取一些資訊
可是當程式執行到

WaitForSingleObject(pboard->open_wait_event, (DWORD)pulse_time);

這一行的時候,Setimer的動作就會暫停,一直到pulse_time結束後才會再繼續,

請問要如何使Setimer的function不受WaitForSingleObject的影響呢 ?

不好意思,打擾了,剛開始學MFC,問題有點多.

Chinson said...

何不就把 SetTimer 寫在主執行緒中呢?

邵馬谷慕 said...

您好,目前就是把Setimer function寫好,在thread裡呼叫(ptp->timer_start();),但執行到WaitForSingleObject還是會暫停

BOOL DCashdrawer::timer_start(void)
{
m_timer_handle = SetTimer(1, m_timer_value, 0);
if (m_timer_handle == 0)
{
return FALSE;
}

return TRUE;
}

打擾了!謝謝

Chinson said...

WaitForSingleObject 目的就是Hold住整個執行緒,如果你仍然要讓 OnTimer 執行 ,可能要由另一個執行緒來啟動 SetTimer 。

邵馬谷慕 said...

您好,不好意思,打擾了,我後來改用CreateThread來建立執行緒, 出現以下error

error C2248:'DCashdrawer::timer_start': cannot access private member declard im class 'DCashdrawer'

一直想不通會什麼會出現此error , 是我寫法錯誤嗎 ?


DWORD WINAPI EventFun(LPVOID lParam)
{
DCashdrawer* ptp = (DCashdrawer*) lParam;

ptp->timer_start();

return TRUE;
}



BOOL DCashdrawer::th_start(void)
{
HANDLE hEventThread = CreateThread(0,0,EventFun,(LPVOID)this,0,NULL);
SetThreadPriority(hEventThread ,THREAD_PRIORITY_LOWEST);

return TRUE;
}

Chinson said...

"cannot access private member declard im class 'DCashdrawer'"

timer_start() 是不是宣告為 DCashdrawer 的 private member?

邵馬谷慕 said...

您好,沒錯,timer_start() 我宣告為 DCashdrawer 的 private member

Chinson said...

你對 C++ 的語法還要更熟悉一點才行喔。
private member 是不能這樣呼叫的,要宣告為 public

Fish said...

大大 您真有耐心! 給你的棒!
台灣需要您這樣的人越多才會更美好!!