MFC の UI スレッドを使用してマネージャー スレッド ワーカー スレッド メカニズムを実装する従来のコードに取り組んでいます。コードは MFC GUI アプリケーションで実行されていましたが、現在は別の dll にあり、GUI アプリケーションとコンソール アプリケーションの両方から実行されます。
マネージャー スレッド、ワーカー スレッド、およびメイン アプリケーションは、スレッド メッセージを介して通信します (ワーカー スレッドは実際にはマネージャー スレッドにメッセージを送信する必要はありませんが、これが元々の実装方法であり、機能していた方法です)。
これで、コンソール アプリからコードを実行すると、メイン スレッドからマネージャー スレッドに送信されたメッセージが処理され、ハンドラーが呼び出されます。問題が発生するのは、マネージャー スレッドからワーカー スレッドにメッセージを送信しようとしたときだけです。への呼び出しはPostThreadMessage
成功しますが、ハンドラーは呼び出されません。この動作は、従来のコンソール アプリケーションと Win32 コンソール アプリケーション (すべての MFC 機能を備えたコンパイル済みヘッダーを含む) の両方で再現されました。
Microsoft の古い記事 ( http://support.microsoft.com/kb/142415)を見つけましたが、よく理解していなかったことを認めざるを得ません。関数をオーバーライドし、PreTranslateMessage
そこでカスタムメッセージを明示的に処理することを示唆しているように試みましたが、呼び出し後に関数が呼び出されることはありませんでしたPostThreadMessage
以下のサンプルで問題を再現しようとしましたが、私のサンプルではマネージャー スレッドへのメッセージでさえ処理されません。
編集:MarsRoverが示唆したように、サンプルコードに欠落していた過負荷を追加し、実際にInitInstance
メッセージExitInstance
がポンプされましたが、メッセージはそうではなく、元のコードで発生していた問題を正確に再現します。サンプルコード:ManagerThread
ManagerThread
WorkerThread
//Common.h
//update the progress message
#define WM_START_RUN (WM_USER + 1)
//update the progress message
#define WM_JOB_DONE (WM_USER + 2)
//run thread has finished
#define WM_RUN (WM_USER + 3)
// ManagerThread.h
class ManagerThread : public CWinThread
{
DECLARE_DYNCREATE(ManagerThread)
protected:
ManagerThread(){} // protected constructor used by dynamic creation
virtual ~ManagerThread();
BOOL InitInstance();
int ExitInstance();
std::vector<WorkerThread*> m_WorkerThreads;
int numOfJobs;
DECLARE_MESSAGE_MAP()
afx_msg void OnStartRun(WPARAM wParam, LPARAM lParam);
afx_msg void OnJobDone(WPARAM wParam, LPARAM lParam);
afx_msg void OnQuit(WPARAM wParam, LPARAM lParam);
};
//WorkerThread.h
class WorkerThread : public CWinThread
{
DECLARE_DYNCREATE(WorkerThread)
protected:
WorkerThread(){} // protected constructor used by dynamic creation
virtual ~WorkerThread(){}
virtual BOOL InitInstance();
virtual int ExitInstance();
public:
void SetManager(CWinThread* pManager) {m_Manager = pManager;}
void SetID(int _id) {id = _id;}
protected:
int id;
CWinThread* m_Manager;
DECLARE_MESSAGE_MAP()
afx_msg void OnRun(WPARAM wParam, LPARAM lParam);
afx_msg void OnQuit(WPARAM wParam, LPARAM lParam);
};
// ManagerThread.cpp
IMPLEMENT_DYNCREATE(ManagerThread, CWinThread)
ManagerThread::~ManagerThread() {
while(!m_WorkerThreads.empty()) {
std::vector<WorkerThread*>::iterator it = m_WorkerThreads.begin();
(*it)->PostThreadMessage(WM_QUIT, 0, 0);
m_WorkerThreads.erase(it);
}
}
BOOL CFilterManagerThread::InitInstance()
{
return CWinThread::InitInstance();
}
int CFilterManagerThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(ManagerThread, CWinThread)
ON_THREAD_MESSAGE(WM_START_RUN, OnStartRun)
ON_THREAD_MESSAGE(WM_JOB_DONE, OnJobDone)
ON_THREAD_MESSAGE(WM_QUIT, OnQuit)
END_MESSAGE_MAP()
void ManagerThread::OnJobDone( WPARAM wParam, LPARAM lParam) {
numOfJobs--;
if (!numOfJobs) {
OnQuit(0,0);
}
}
void ManagerThread::OnStartRun(WPARAM wParam, LPARAM lParam) {
numOfJobs = (int) wParam;
for (int i = 0; i < numOfJobs; i++) {
WorkerThread *newThread = (WorkerThread*)AfxBeginThread(RUNTIME_CLASS(WorkerThread), THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED);
newThread->SetID(i);
newThread->SetManager(this);
m_WorkerThreads.push_back(newThread);
newThread->ResumeThread();
Sleep(1000); //sleep 1 second before sending message to allow the thread to strat running
newThread->PostThreadMessage(WM_RUN, 0, 0);
}
}
void ManagerThread::OnQuit(WPARAM wParam, LPARAM lParam) {
AfxEndThread(0);
}
// WorkerThread.cpp
IMPLEMENT_DYNCREATE(WorkerThread, CWinThread)
BOOL WorkerThread::InitInstance() {
// TODO: perform and per-thread initialization here
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
return TRUE;
}
int WorkerThread::ExitInstance() {
// TODO: perform any per-thread cleanup here
//uninitialize the COM library
CoUninitialize();
return CWinThread::ExitInstance();
}
BEGIN_MESSAGE_MAP(WorkerThread, CWinThread)
ON_THREAD_MESSAGE(WM_RUN, OnRun)
ON_THREAD_MESSAGE(WM_QUIT, OnQuit)
END_MESSAGE_MAP()
void WorkerThread::OnRun(WPARAM wParam, LPARAM lParam) {
cout << id <<endl;
m_Manager->PostThreadMessage(WM_JOB_DONE, id, 0);
}
void WorkerThread::OnQuit(WPARAM wParam, LPARAM lParam) {
AfxEndThread(0);
}
とでmain
:
ManagerThread *manager = (ManagerThread*)AfxBeginThread(RUNTIME_CLASS(ManagerThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
manager->ResumeThread();
Sleep(1000); //sleep 1 second before sending message to allow the thread to start running
manager->PostThreadMessage(WM_START_RUN, 10, 0);
while(true){}
これは大まかなサンプルです。もちろん、私の元のコードでは、同期を確実にし、マネージャー スレッドが終了する前にプログラムが終了するのを避けるために、 andSleep
よりも優れたメカニズムを使用しています。while(true)
しかし、それは私が抱えている問題を再現するので、これ以上複雑にする意味がわかりませんでした。