WaitForSingleObject、WaitForMultipleObjects假死,永远等待的问题

做了一个运行程序,然后等待程序结束的函数,大体流程如下:

伪代码:
CreateProcess(...) -> processHandle;
...
WaitForSingleObject(processHandle);

发现CreateProcess创建的进程已经结束了,但WaitForSingleObject还一直在等待。

更加诡异的是,这个问题在Release里没有发生,在Debug里就发生了!

查MSDN:https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject

后发现了问题:

Parameters

hHandle

A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.

If this handle is closed while the wait is still pending, the function's behavior is undefined.

The handle must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.

 注意到红色的字没有,大体意思是如果在WaitFor的过程中等待的句柄被关闭了,则WaitFor则会出现各种未知错误。再结合只在Debug的情况下发生,我即刻想到了这种情况:

CreateProcess -->  进程结束了  -->   WaitFor 

 注意进程结束了吧,进程结束后processHandle就不可能改变状态了,也就是随后给的WaitFor传入的Handle不可能改变状态了,所以WaitFor就会一直等下去。

搞清楚问题所在,把过程改为:创建一个线程监听进程IO,程序结束后,IO结束,再配合waitFor event,就可以解决问题了。

最后附上我的创建进程的函数:

unsigned __stdcall _Execute_readAndWrite(void* arg)
{
	std::tuple<HANDLE, std::string*, HANDLE>* tpParams = (std::tuple<HANDLE, std::string*, HANDLE>*)arg;
	HANDLE hRead = std::get<0>(*tpParams);
	std::string* sPrintText = std::get<1>(*tpParams);
	HANDLE ev = std::get<2>(*tpParams);

	//读取命令行返回值
	char buff[1024 + 1];
	DWORD dwRead = 0;
	while (ReadFile(hRead, buff, 1024, &dwRead, NULL))
	{
		if (sPrintText)
		{
			buff[dwRead] = '\0';
			sPrintText->append(buff, dwRead);
		}
	}

	SetEvent(ev);

	return 0;
}

bool Execute(const char* szFile, const char* szParam, unsigned long& exitCode, std::string* sPrintText = NULL, unsigned long timeout = 0)
{
	if (!szFile || szFile[0] == '\0')
		return false;

	HANDLE hRead, hWrite;
	//创建匿名管道
	SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
	if (!CreatePipe(&hRead, &hWrite, &sa, 0))
	{
		return false;
	}

	int nCmdLen = (strlen(szFile) + strlen(szParam) + 4) * sizeof(char);
	char* szCmd = (char*)_alloca(nCmdLen);//_alloca在栈上申请的,会自动释放
	memset(szCmd, 0, nCmdLen);
	strcpy(szCmd, "\"");
	strcat(szCmd, szFile);
	strcat(szCmd, "\"");
	if (szParam)
	{
		strcat(szCmd, " ");
		strcat(szCmd, szParam);
	}

	//设置命令行进程启动信息(以隐藏方式启动命令并定位其输出到hWrite)
	STARTUPINFOA si = { sizeof(STARTUPINFOA) };
	GetStartupInfoA(&si);
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	si.wShowWindow = SW_NORMAL;
	si.hStdError = hWrite;
	si.hStdOutput = hWrite;

	//启动命令行
	PROCESS_INFORMATION pi;
	if (!CreateProcessA(NULL, (char *)szCmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
	{
		CloseHandle(hWrite);
		CloseHandle(hRead);
		return false;
	}

	//立即关闭hWrite
	CloseHandle(hWrite);
	
	HANDLE ev = CreateEventA(NULL, TRUE, FALSE, NULL);

	bool bRet = true;

	unsigned int uiThreadID = 0;
	HANDLE hThreadRW = (HANDLE)_beginthreadex(NULL, 0, _Execute_readAndWrite, 
		(void*)&(std::tuple<HANDLE, std::string*, HANDLE>(hRead, sPrintText, ev)), 0, &uiThreadID);
	
	DWORD waitRet = 0;
	if (timeout > 0)
		waitRet = WaitForSingleObject(ev, timeout);
	else
		waitRet = WaitForSingleObject(ev, INFINITE);

	switch (waitRet)
	{
	case WAIT_TIMEOUT:
	case WAIT_FAILED:
		TerminateThread(hThreadRW, 1);
		TerminateProcess(pi.hProcess, 1);
		bRet = false;
		break;
	case WAIT_OBJECT_0:		
		GetExitCodeProcess(pi.hProcess, &exitCode);//获得返回值
		bRet = true;
		break;
	}

	CloseHandle(hRead);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	CloseHandle(ev);

	return bRet;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值