C++ Builder 10.2: Thread blocks WaitForInputIdle
C++ Builder 10.2: Thread blocks WaitForInputIdle
>我有以下情况:应用程序 B 应该启动应用程序 A,然后等待 A 变为空闲状态。为此,B 将 CreateProcess 与 WaitForInputIdle 结合使用,不幸的是,此命令的超时已设置为无限。
这是 B 源代码的相应部分:
void StartA(void){
STARTUPINFO siInfo;
PROCESS_INFORMATION piInfo;
ZeroMemory(&siInfo, sizeof(siInfo));
ZeroMemory(&piInfo, sizeof(piInfo));
CreateProcess(L"A.exe", L"", 0, 0,
false, CREATE_DEFAULT_ERROR_MODE, 0, 0,
&siInfo, &piInfo);
WaitForInputIdle(piInfo.hProcess, INFINITE); // A will block this command!
CloseHandle(piInfo.hProcess);
CloseHandle(piInfo.hThread);
}
当我从这个应用程序 B 调用我的应用程序 A 时,由于 A 的 TestThread 调用了 SendMessage 命令,B 将被其 WaitForInputIdle 命令永远阻止。如果我只在 FormShow 事件之后创建 TestThread,那么 WaitForInputIdle 将按预期返回。这是一个解决方案,虽然我知道文章 WaitForInputIdle 等待任何线程,这可能不是您关心的线程,但我喜欢了解这里实际发生的事情。
这是应用程序 A 的简化源代码。它只是由一个TForm和TestThread类组成,TestThread是从TThread派生出来的。
class TestThread : public TThread {
public:
__fastcall TestThread(HWND in_msg) : msg(in_msg), TThread(false) {};
virtual __fastcall ~TestThread(){};
private:
void __fastcall Execute(){
// Next line leads to WaitForInputIdle blocking
SendMessage(msg, WM_USER, NULL, NULL);
while(!Terminated) Sleep(1);
}
HWND msg;
};
class TFormA : public TForm{
private:
TestThread * testthread_p;
public:
__fastcall TFormA(TComponent* Owner){
testthread_p = new TestThread(Handle);
}
virtual __fastcall ~TFormA(){}
};
为什么命令 WaitForInputIdle 无法检测到应用程序 A 的空闲状态?
启动的应用程序永远不会有机会进入"输入空闲"状态。
假设TFormA
是应用程序的MainForm
,它在应用程序启动时创建,从而在VCL的主UI消息循环开始运行之前创建线程(当WinMain()
调用Application->Run()
时)。
即使工作线程使用的是SendMessage()
而不是PostMessage()
,它也在跨线程边界发送消息,因此在接收线程(在本例中为主 UI 线程)调用(Peek|Get)Message()
之前,每条消息都不会调度到窗口。 这在SendMessage()
文档中有说明:
仅当接收线程执行消息检索代码时,才会处理线程之间发送的消息。发送线程将被阻止,直到接收线程处理消息。
当主 UI 消息循环开始运行时,来自线程的一条消息已经发送到窗口,正在等待调度。 发送后续消息时,它们之间的延迟为 1 毫秒。 因此,当主 UI 消息循环第一次尝试从队列中检索消息时,已经有一条消息在等待。当循环调度该消息进行处理并返回到队列以等待消息时,已经有一条新消息在等待它。
WaitForInputIdle()
文档说:
WaitForInputIdle
函数使线程能够暂停其执行,直到指定的进程完成其初始化并等待用户输入,没有输入挂起。如果进程有多个线程,则只要任何线程空闲,WaitForInputIdle
函数就会返回。
主 UI 线程具有来自线程的连续消息挂起,因此它不能变为"输入空闲"。工作线程不会检索自己的入站消息,因此它也不能成为"输入空闲"(请参阅 WaitForInputIdle 等待任何线程,这可能不是你关心的线程)。
因此,应用程序进程作为一个整体永远不会变得"输入空闲",因此WaitForInputIdle()
阻塞直到其超时过去,在这种情况下是INFINITE
,因此它无限期地阻塞。
窗体的OnShow
事件直到主 UI 消息循环运行很久之后才会触发,并且已经处理了其他几个窗口消息,因此应用有时间进入"输入空闲",取消阻止WaitForInputIdle()
,然后创建线程并开始向窗口发送消息。
- 在std::thread中,joinable()然后join()线程安全吗
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 分离一个静态常量 std::thread?
- 尝试使用 std::vector<std::thread时出现静态断言失败错误>
- 使用 thread 类在 C++ 中构造线程的动态数组时出错
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何从 std::thread 返回值
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 将 std::thread by 值推送到列表中
- 转发变量参数列表以模拟 std::thread
- 嵌入式设备 -> std::thread -> FreeRTOS?
- 对 'std::thread::_M_start_thread CMake 的未定义引用进行基准测试
- std::thread 增加 DLL 引用计数,从而防止卸载 DLL
- 如何防止 std::thread 在 QT 中冻结 GUI?
- 对带有唯一指针的 std::thread 使用类成员函数时出现编译错误
- C++ - Thread Pool
- 为什么参数在构造 std::thread 时移动两次
- std::thread::_Invoker 使用线程编程时出错
- 如何更改输出的位置或光标在输出处,以便在"Code Blocks"中C++
- C++ Builder 10.2: Thread blocks WaitForInputIdle