在我的代码中,我使用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
是实现目标的正确方法(==&gt;请参阅我的其他question):
如果我应该用其他方法来中断主线程:
WaitForSingleObject
或轮询。我想要额外的线程 push -es它的数据会尽快直接进入主线程。)答案 0 :(得分:12)
是的,QueueUserAPC不是这里的解决方案。它的回调只会在线程阻塞并且程序员明确允许等待是警报时运行。那不太可能。
我对发布解决方案犹豫不决,因为它会给你带来巨大的麻烦。您可以使用SuspendThread(),GetThreadContext(),SetThreadContext()和ResumeThread()实现线程中断。关键是将CONTEXT.Eip值保存在线程的调用堆栈中,并将其替换为中断函数的地址。
你不能做这项工作的原因是因为你会有可怕的重入问题。你无法猜测你将在哪个执行点中断线程。它可能正好在它改变状态的中间,你需要如此糟糕的状态,你正在考虑这样做。没有办法不陷入这个陷阱,你不能用互斥锁或诸如此类的东西来阻止它。它也很难诊断,因为它会长时间工作,然后在中断时间恰好不幸时随机失败。
线程必须处于众所周知的状态才能安全地运行注入的代码。之前已经多次提到传统的一个:当一个线程正在抽取一个消息循环时,它是隐式空闲的,并没有做任何危险的事情。 QueueUserAPC具有相同的方法,一个线程明确地向操作系统发出信号,表明它是一个可以安全执行回调的状态。两者都是阻塞(不执行危险代码)和设置bAlertable标志。
线程必须明确表示它处于安全状态。没有安全的推模型,只有拉动。
答案 1 :(得分:4)
根据我在MSDN中可以理解的,在线程进入可警告状态之前不会调用回调,这可以通过调用SleepEx
,SignalObjectAndWait
,{{1}来完成} WaitForSingleObjectEx,
或WaitForMultipleObjectsEx
。
所以如果你真的不想做一些民意调查,我认为这种方法不适合你的情况。
是否可以在主线程中实现“消息泵”(或者更确切地说是事件侦听器)并将其所有当前工作委托给另一个线程?在这种情况下,主线程等待其他线程设置的任何事件。