4 Stimmen

Callback, angegeben in QueueUserAPC , wird nicht aufgerufen

In meinem Code verwende ich QueueUserAPC a unterbrechen den Haupt-Thread von seiner aktuellen Arbeit zu trennen, um zuerst einen Rückruf aufzurufen, bevor er zu seiner vorherigen Arbeit zurückkehrt.

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 y startSecondThread sind einem Lua-Interpreter ausgesetzt, der zusammen mit anderen Dingen im Hauptthread läuft. Was ich jetzt mache, ist

  1. aufrufen. storeHandle von meinem Lua-Interpreter. DuplicateHandle einen Wert ungleich Null zurück und ist daher erfolgreich.
  2. aufrufen. startSecondThread von meinem Lua-Interpreter. Der zusätzliche Thread wird ordnungsgemäß gestartet, und QueueUserAPC einen Wert ungleich Null zurück, was bedeutet, dass alles gut gelaufen ist.
  3. soweit ich verstanden habe QueueUserAPC , myCallback sollte nun vom Hauptthread aufgerufen werden. Das ist jedoch nicht der Fall.

Si QueueUserAPC ist der richtige Weg, um mein Ziel zu erreichen (==> siehe meine andere Frage ):

  • Wie kann ich das zum Laufen bringen?

Wenn ich eine andere Methode, um den Haupt-Thread zu unterbrechen sollte:

  • Welche andere Methode sollte ich anwenden? (Beachten Sie, dass ich nicht verwenden möchte ziehen -Methode im Haupt-Thread für diesen Zweck wie WaitForSingleObject oder Umfragen. Ich möchte, dass der zusätzliche Thread drücken. -seine Daten so schnell wie möglich direkt in den Hauptthread).

12voto

Hans Passant Punkte 894572

Ja, QueueUserAPC ist nicht die Lösung hier. Sein Callback wird nur ausgeführt, wenn der Thread blockiert und der Programmierer ausdrücklich erlaubt hat, dass die Wartezeit alarmierbar ist. Das ist unwahrscheinlich.

Ich zögere, die Lösung zu veröffentlichen, weil sie Sie in große Schwierigkeiten bringen wird. Sie können eine Thread-Unterbrechung mit SuspendThread(), GetThreadContext(), SetThreadContext() und ResumeThread() implementieren. Der Schlüssel ist, den Wert CONTEXT.Eip auf dem Aufrufstapel des Threads zu speichern und ihn durch die Adresse der Unterbrechungsfunktion zu ersetzen.

Der Grund dafür, dass dies nicht funktioniert, ist, dass Sie schreckliche Probleme mit der Wiederverknüpfung haben werden. Es gibt keine Möglichkeit zu erraten, an welchem Punkt der Ausführung Sie den Thread unterbrechen werden. Es kann durchaus sein, dass er sich mitten in der Mutation befindet, also in dem Zustand, den Sie so dringend brauchen, dass Sie dies in Erwägung ziehen. Es gibt keine Möglichkeit, nicht in diese Falle zu tappen, man kann sie nicht mit einer Mutex oder ähnlichem blockieren. Es ist auch extrem schwer zu diagnostizieren, weil es so lange gut funktioniert und dann zufällig versagt, wenn das Interrupt-Timing unglücklich ist.

Ein Thread muss sich in einem wohlbekannten Zustand befinden, bevor er den injizierten Code sicher ausführen kann. Der traditionelle Zustand wurde schon oft erwähnt: Wenn ein Thread eine Nachrichtenschleife pumpt, ist er implizit im Leerlauf und tut nichts Gefährliches. QueueUserAPC verfolgt denselben Ansatz: Ein Thread signalisiert dem Betriebssystem explizit, dass er sich in einem Zustand befindet, in dem der Callback sicher ausgeführt werden kann. Sowohl durch Blockieren (keine Ausführung von gefährlichem Code) als auch durch Setzen des bAlertable-Flags.

Ein Thread muss ausdrücklich signalisieren, dass er sich in einem sicheren Zustand befindet. Es gibt kein sicheres Push-Modell, nur Pull.

4voto

cedrou Punkte 2742

Soweit ich das verstehen kann, ist in MSDN wird der Callback erst dann aufgerufen, wenn der Thread in einen alarmierbaren Zustand übergeht, und zwar durch den Aufruf SleepEx , SignalObjectAndWait , WaitForSingleObjectEx, WaitForMultipleObjectsEx , oder MsgWaitForMultipleObjectsEx .

Wenn Sie also wirklich keine Umfragen durchführen wollen, ist diese Methode meiner Meinung nach nicht für Ihren Fall geeignet.

Ist es möglich, eine "Message-Pump" (oder eher einen Event-Listener) in Ihrem Haupt-Thread zu implementieren und seine gesamte aktuelle Arbeit an einen anderen Thread zu delegieren? In diesem Fall wartet der Hauptthread auf alle Ereignisse, die von den anderen Threads gesetzt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X