1

MFC の UI スレッドを使用してマネージャー スレッド ワーカー スレッド メカニズムを実装する従来のコードに取り組んでいます。コードは MFC GUI アプリケーションで実行されていましたが、現在は別の dll にあり、GUI アプリケーションとコンソール アプリケーションの両方から実行されます。

マネージャー スレッド、ワーカー スレッド、およびメイン アプリケーションは、スレッド メッセージを介して通信します (ワーカー スレッドは実際にはマネージャー スレッドにメッセージを送信する必要はありませんが、これが元々の実装方法であり、機能していた方法です)。

これで、コンソール アプリからコードを実行すると、メイン スレッドからマネージャー スレッドに送信されたメッセージが処理され、ハンドラーが呼び出されます。問題が発生するのは、マネージャー スレッドからワーカー スレッドにメッセージを送信しようとしたときだけです。への呼び出しはPostThreadMessage成功しますが、ハンドラーは呼び出されません。この動作は、従来のコンソール アプリケーションと Win32 コンソール アプリケーション (すべての MFC 機能を備えたコンパイル済みヘッダーを含む) の両方で再現されました。

Microsoft の古い記事 ( http://support.microsoft.com/kb/142415)を見つけましたが、よく理解していなかったことを認めざるを得ません。関数をオーバーライドし、PreTranslateMessageそこでカスタムメッセージを明示的に処理することを示唆しているように試みましたが、呼び出し後に関数が呼び出されることはありませんでしたPostThreadMessage

以下のサンプルで問題を再現しようとしましたが、私のサンプルではマネージャー スレッドへのメッセージでさえ処理されません。

編集:MarsRoverが示唆したように、サンプルコードに欠落していた過負荷を追加し、実際にInitInstanceメッセージExitInstanceがポンプされましたが、メッセージはそうではなく、元のコードで発生していた問題を正確に再現します。サンプルコード:ManagerThreadManagerThreadWorkerThread

//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)しかし、それは私が抱えている問題を再現するので、これ以上複雑にする意味がわかりませんでした。

4

1 に答える 1

2

問題が何であるかを理解しました。CoInitializeEx問題はinへの呼び出しでしたWorkerThread::initInstance。どうやら呼び出しは、サンプル コードの Sleep(1000) よりも長い時間、スレッドの初期化をブロックしたようです。そのため、メッセージ キューが作成される前にメッセージを投稿していました。したがって、MSDNの指示に従います。

メッセージがポストされるスレッドは、メッセージ キューを作成している必要があります。そうでない場合、PostThreadMessage の呼び出しは失敗します。この状況を処理するには、次の方法を使用します。

イベント オブジェクトを作成してから、スレッドを作成します。

PostThreadMessage を呼び出す前に、WaitForSingleObject 関数を使用して、イベントがシグナル状態に設定されるのを待ちます。

メッセージがポストされるスレッドで、次に示すように PeekMessage を呼び出して、システムにメッセージ キューを作成させます。

PeekMessage(&msg、NULL、WM_USER、WM_USER、PM_NOREMOVE)

イベントを設定して、スレッドが投稿されたメッセージを受信する準備ができていることを示します。

この前の質問CEventから、スレッド クラスごとにメンバーを作成し、次のように変更しInitInstanceました。

BOOL CFilterWorkerThread::InitInstance()
{
    BOOL worked=CWinThread::InitInstance();
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    MSG msg;
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    m_ControllerThreadReady.SetEvent();
    return TRUE;
}

イベントを true に設定する前に、メッセージ キューを強制的に初期化するためです。次に、にメッセージを投稿する前ManagerThreadに呼び出します。WaitForSingleObjectWorkerThread

于 2012-10-08T13:31:18.990 に答える