天天看点

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;
}