问题描述
在我的代码中,我使用QueueUserAPC
从他当前的工作中中断主线程,以便在返回他之前的工作之前先调用一些回调.
In my code, I use QueueUserAPC
to interrupt the main thread from his current work in order to invoke some callback first before going back to his previous work.
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解释器,该解释器与其他内容一起在主线程中运行.我现在要做的是
storeHandle
and startSecondThread
are exposed to a Lua interpreter which is running in the main thread along with other things. What I do now, is
- 从我的Lua解释器中调用
storeHandle
.DuplicateHandle
返回非零值,因此成功. - 从我的Lua解释器中调用
startSecondThread
.附加线程正确启动,并且QueueUserAPC
返回一个非零值,表明一切正常. 据我了解的 -
QueueUserAPC
,现在应该从主线程调用myCallback
了.但是,事实并非如此.
- invoke
storeHandle
from my Lua interpreter.DuplicateHandle
returns a non-zero value and therefore succeeds. - invoke
startSecondThread
from my Lua interpreter. The additional thread gets started properly, andQueueUserAPC
returns a nonzero value, stating, that all went well. - as far as I understood
QueueUserAPC
,myCallback
should now get called from the main thread. However, it doesn't.
如果QueueUserAPC
是实现我的目标的正确方法(==>参见我的其他):
If QueueUserAPC
is the correct way to accomplish my goal (==> see my other question):
- 我该如何工作?
如果我应该使用其他方法来中断主线程:
If I should some other method to interrupt the main thread:
- 我还应该使用其他什么方法? (请注意,我不想在主线程中使用 pull -ing这样的方法,例如
WaitForSingleObject
或轮询.我希望其他线程 push -es尽快将数据直接导入主线程.)
- What other method should I use? (Note that I don't want to use pull-ing method in the main thread for this like
WaitForSingleObject
or polling. I want that the additional thread push-es it's data straight into the main thread, as soon as possible.)
推荐答案
是的,这里不是QueueUserAPC.它的回调仅在线程阻塞并且程序员明确允许等待警报时运行.不太可能.
Yeah, QueueUserAPC is not the solution here. Its callback will only run when the thread blocks and the programmer has explicitly allowed the wait to be alertable. That's unlikely.
我不愿意发布解决方案,因为它会使您陷入巨大麻烦.您可以使用SuspendThread(),GetThreadContext(),SetThreadContext()和ResumeThread()实现线程中断.关键是将CONTEXT.Eip值保存在线程的调用堆栈上,并将其替换为中断函数的地址.
I hesitate to post the solution because it is going to get you into enormous trouble. You can implement a thread interrupt with SuspendThread(), GetThreadContext(), SetThreadContext() and ResumeThread(). The key is to save the CONTEXT.Eip value on the thread's call stack and replace it with the address of the interrupt function.
之所以无法完成这项工作,是因为您将面临可怕的重新进入问题.您无法猜测将在哪个执行点中断线程.它很可能正处于突变状态的中间,因为您非常需要这种状态,以至于您正在考虑这样做.没有办法不落入这个陷阱,您不能用互斥或其他方式阻止它.诊断也非常困难,因为它会运行这么长时间,然后在中断时间不巧的情况下随机失败.
The reason you cannot make this work is because you'll have horrible re-entrancy problems. There is no way you can guess at which point of execution you'll interrupt the thread. It may well be right in the middle of it mutating state, the state that you need so badly that you are contemplating doing this. There is no way to not fall into this trap, you can't block it with a mutex or whatnot. It is also extremely hard to diagnose because it will work so well for so long, then randomly fail when the interrupt timing just happens to be unlucky.
线程必须处于众所周知的状态,才能安全地运行注入的代码.以前已经多次提到传统的方法:当线程正在泵送消息时,消息循环隐式处于空闲状态,并且不做任何危险的事情. QueueUserAPC具有相同的方法,线程明确向操作系统发出信号,表明该状态是可以安全执行回调的状态.通过阻止(不执行危险代码)和设置bAlertable标志来实现.
A thread must be in a well known state before it can safely run injected code. The traditional one has been mentioned many times before: when a thread is pumping a message loop is is implicitly idle and not doing anything dangerous. QueueUserAPC has the same approach, a thread explicitly signals the operating system that it is a state where the callback can be safely executed. Both by blocking (not executing dangerous code) and setting the bAlertable flag.
线程必须显式表示它处于安全状态.没有安全的推模型,只有拉.
A thread has to explicitly signal that it is in a safe state. There is no safe push model, only pull.
这篇关于在QueueUserAPC中指定的回调不会被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!