4

私のコードでは、前の作業に戻る前に最初にコールバックを呼び出すために、現在の作業からメインスレッドを中断QueueUserAPCするために使用します。

std::string buffer;
std::tr1::shared_ptr<void> hMainThread;
VOID CALLBACK myCallback (ULONG_PTR dwParam) {
    FILE * f = fopen("somefile", "a");
    fprintf(f, "CALLBACK WAS INVOKED!\n");
    fclose(f);
}
void AdditionalThread () {
    // download some file using synchronous wininet and store the
    // HTTP response in buffer
    QueueUserAPC(myCallback, hMainThread.get(), (ULONG_PTR)0);
}
void storeHandle () {
    HANDLE hUnsafe;
    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 
        GetCurrentProcess(), &hUnsafe, 0, FALSE, DUPLICATE_SAME_ACCESS);
    hMainThread.reset(hUnsafe, CloseHandle);
}
void startSecondThread () {
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AdditionalThread, 0, 0, NULL);
}

storeHandleそしてstartSecondThread、他のものと一緒にメインスレッドで実行されているLuaインタープリターにさらされています。私が今していることは

  1. storeHandle私のLuaインタプリタから呼び出します。DuplicateHandleゼロ以外の値を返すため、成功します。
  2. startSecondThread私のLuaインタプリタから呼び出します。追加のスレッドは適切に開始され、QueueUserAPCすべてがうまくいったことを示すゼロ以外の値を返します。
  3. 私が理解している限りQueueUserAPCmyCallbackメインスレッドから呼び出されるはずです。ただし、そうではありません。

QueueUserAPCが私の目標を達成するための正しい方法である場合(==>他の質問を参照):

  • どうすればこれを機能させることができますか?

メインスレッドを中断する他の方法が必要な場合:

  • 他にどのような方法を使用する必要がありますか?(このようなポーリングやポーリングのためにメインスレッドでpull -ingメソッドを使用したくないことに注意してくださいWaitForSingleObject。追加のスレッドがデータをできるだけ早くメインスレッドに直接プッシュするようにします。)
4

2 に答える 2

12

ええ、QueueUserAPC はここでは解決策ではありません。そのコールバックは、スレッドがブロックされ、プログラマーが待機をアラート可能にすることを明示的に許可した場合にのみ実行されます。それはありそうもありません。

解決策を投稿するのをためらっています。SuspendThread()、GetThreadContext()、SetThreadContext()、および ResumeThread() を使用してスレッド割り込みを実装できます。重要なのは、CONTEXT.Eip 値をスレッドのコール スタックに保存し、それを割り込み関数のアドレスに置き換えることです。

これが機能しない理由は、恐ろしい再入可能性の問題が発生するためです。実行のどの時点でスレッドを中断するかを推測する方法はありません。それは変異状態の真っ只中にある可能性があり、あなたがこれを行うことを考えているほどひどく必要な状態です. このトラップに陥らないようにする方法はありません。ミューテックスなどでブロックすることはできません。また、非常に長い間うまく機能し、割り込みタイミングがたまたま運が悪かった場合にランダムに失敗するため、診断が非常に困難です。

挿入されたコードを安全に実行するには、スレッドが既知の状態である必要があります。伝統的なものは、これまで何度も言及されてきました。スレッドがメッセージ ループをポンピングしているとき、メッセージ ループは暗黙的にアイドル状態であり、危険なことは何もしていません。QueueUserAPC にも同じアプローチがあり、スレッドは、コールバックを安全に実行できる状態であることをオペレーティング システムに明示的に通知します。ブロックする (危険なコードを実行しない) ことと、bAlertable フラグを設定することの両方によって。

スレッドは、安全な状態であることを明示的に通知する必要があります。安全なプッシュ モデルはなく、プルのみです。

于 2009-12-30T18:11:01.907 に答える
4

MSDNで理解できることから、コールバックは、スレッドがアラート可能な状態になるまで呼び出されません。これは、、、、またはを呼び出すことによってSleepEx行われSignalObjectAndWaitます。WaitForSingleObjectEx, WaitForMultipleObjectsExMsgWaitForMultipleObjectsEx

したがって、本当にポーリングを行いたくない場合は、この方法があなたのケースに適しているとは思いません。

メインスレッドに「メッセージポンプ」(またはイベントリスナー)を実装し、現在のすべての作業を別のスレッドに委任することは可能ですか? この場合、メイン スレッドは、他のスレッドによって設定されたイベントを待機します。

于 2009-12-30T14:56:19.713 に答える