【问题标题】:Callback, specified in QueueUserAPC , does not get called在 QueueUserAPC 中指定的回调不会被调用
【发布时间】:2009-12-30 13:14:50
【问题描述】:

在我的代码中,我使用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);
}

storeHandlestartSecondThread 暴露给 Lua 解释器,该解释器与其他东西一起在主线程中运行。我现在做的是

  1. 从我的 Lua 解释器调用 storeHandleDuplicateHandle 返回一个非零值,因此成功。
  2. 从我的 Lua 解释器调用 startSecondThread。附加线程正常启动,QueueUserAPC 返回一个非零值,表示一切顺利。
  3. 据我了解QueueUserAPCmyCallback 现在应该从主线程调用。然而,事实并非如此。

如果QueueUserAPC 是实现我的目标的正确方法(==> 见我的另一个question):

  • 我怎样才能让它工作?

如果我应该使用其他方法来中断主线程:

  • 我应该使用什么其他方法? (请注意,我不想在主线程中使用 pull-ing 方法,例如 WaitForSingleObject 或轮询。我希望附加线程 push-es它的数据会尽快直接进入主线程。)

【问题讨论】:

    标签: c++ windows multithreading signals interrupt


    【解决方案1】:

    是的,QueueUserAPC 不是这里的解决方案。它的回调只会在线程阻塞并且程序员明确允许等待是警报时运行。这不太可能。

    我不愿发布解决方案,因为它会给您带来巨大的麻烦。您可以使用 SuspendThread()、GetThreadContext()、SetThreadContext() 和 ResumeThread() 实现线程中断。关键是把CONTEXT.Eip的值保存在线程的调用栈上,替换成中断函数的地址。

    您无法完成这项工作的原因是您将遇到可怕的重入问题。您无法猜测您将在哪个执行点中断线程。它很可能正处于变异状态的中间,你非常需要这种状态,以至于你正在考虑这样做。没有办法不落入这个陷阱,你不能用互斥锁之类的东西来阻止它。它也非常难以诊断,因为它会在很长一段时间内运行良好,然后在中断时间恰好不走运时随机失败。

    线程必须处于众所周知的状态才能安全地运行注入的代码。之前已经多次提到过传统的:当一个线程正在泵送一个消息循环时,它是隐式空闲的,并且没有做任何危险的事情。 QueueUserAPC 具有相同的方法,线程显式地向操作系统发出信号,表明它是可以安全执行回调的状态。通过阻塞(不执行危险代码)和设置 bAlertable 标志。

    线程必须明确表示它处于安全状态。没有安全的推送模型,只有拉取。

    【讨论】:

      【解决方案2】:

      根据我在MSDN 中的理解,在线程进入可警报状态之前不会调用回调,这是通过调用SleepExSignalObjectAndWaitWaitForSingleObjectEx, WaitForMultipleObjectsEx 或@987654326 来完成的@。

      所以如果你真的不想做一些投票,我认为这种方法不适合你的情况。

      是否可以在您的主线程中实现“消息泵”(或者更确切地说是事件监听器)并将其所有当前工作委托给另一个线程?在这种情况下,主线程等待其他线程设置的任何事件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-28
        • 2013-11-23
        • 1970-01-01
        相关资源
        最近更新 更多