在我的代码中,我使用 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 解释器以及其他东西。我现在要做的是storeHandle
。 DuplicateHandle
返回一个非零值,因此成功。 startSecondThread
。附加线程正确启动,QueueUserAPC
返回一个非零值,说明一切顺利。 QueueUserAPC
,myCallback
。然而,事实并非如此。 如果
QueueUserAPC
是实现我的目标的正确方法(==> 查看我的其他 question ):如果我应该使用其他方法来中断主线程:
WaitForSingleObject
或轮询。我希望附加线程尽快将其数据直接推送到主线程中。) 最佳答案
是的,QueueUserAPC 不是这里的解决方案。它的回调只会在线程阻塞并且程序员明确允许等待是可警报的时候运行。那不太可能。
我对发布解决方案犹豫不决,因为它会给你带来巨大的麻烦。您可以使用 SuspendThread()、GetThreadContext()、SetThreadContext() 和 ResumeThread() 实现线程中断。关键是将CONTEXT.Eip值保存在线程的调用栈上,并替换为中断函数的地址。
您无法完成这项工作的原因是因为您将遇到可怕的重入问题。您无法猜测将在哪个执行点中断线程。它很可能正处于变异状态的中间,您非常需要这种状态,以至于您正在考虑这样做。没有办法不落入这个陷阱,你不能用互斥锁或诸如此类的东西来阻止它。它也非常难以诊断,因为它会工作这么长时间,然后在中断时间恰好不走运时随机失败。
线程必须处于众所周知的状态才能安全地运行注入(inject)的代码。传统的一个之前已经多次提到:当一个线程正在泵送消息循环时,它是隐式空闲的并且不做任何危险的事情。 QueueUserAPC 具有相同的方法,线程显式地向操作系统发出信号,表明它处于可以安全执行回调的状态。通过阻塞(不执行危险代码)和设置 bAlertable 标志。
线程必须明确表示它处于安全状态。没有安全的推模型,只有拉。