【发布时间】:2011-03-11 22:40:44
【问题描述】:
我正在使用 _beginthread 在我的应用程序中使用多线程,现在要等到所有线程都完成我有全局布尔值,当每个线程完成时我都设置为 true,所以在那之前我处于一个 while 循环中。必须有更清洁的方法吗?
谢谢
【问题讨论】:
标签: c++ c windows multithreading
我正在使用 _beginthread 在我的应用程序中使用多线程,现在要等到所有线程都完成我有全局布尔值,当每个线程完成时我都设置为 true,所以在那之前我处于一个 while 循环中。必须有更清洁的方法吗?
谢谢
【问题讨论】:
标签: c++ c windows multithreading
您可以使用WaitForMultipleObjects 等待主线程中的线程完成。
【讨论】:
您想了解的是线程同步技术——幸运的是,MSDN 上有相当多的信息可能会对您有所帮助。您可能想要使用 Events 和 WaitHandles,这是 MSDN 上的主要内容:http://msdn.microsoft.com/en-us/library/ms681924%28v=VS.85%29.aspx 有很多示例。
还有一些关于 MFC 中的同步的信息(可能有用也可能没用,添加以供参考):http://msdn.microsoft.com/en-us/library/975t8ks0%28VS.71%29.aspx
我进行了一些搜索,但我很难找到一些对您有用的信息,这些信息不使用 MFC 实现。这里有一个很好的教程 (http://www.informit.com/library/content.aspx?b=Visual_C_PlusPlus&seqNum=149),但是,再次使用 MFC。您可以先看看 MFC 的互斥锁实现。
因此,您需要熟悉同步函数和结构 - MSDN 上的所有内容:http://msdn.microsoft.com/en-us/library/ms686679%28v=VS.85%29.aspx
【讨论】:
请改用_beginthreadex。 _beginthread 和 _beginthreadex return a thread handle 都有,但是以_beginthread 开头的线程在完成时会自动关闭其句柄,因此使用它进行同步并不可靠。
线程句柄可以与Win32的其中一种同步函数一起使用,例如WaitForSingleObject或WaitForMultipleObjects。
完成后,_beginthreadex 返回的句柄必须用CloseHandle() 关闭。
【讨论】:
通常的方法是保留所有线程句柄,然后等待每个句柄。当句柄发出信号时,线程已完成,因此从线程集中移除。我使用std::set<HANDLE> 来跟踪线程句柄。在 Windows 中等待多个对象有两种不同的方法:
WaitForSingleObject,每个都超时WaitForMultipleObjects
第一个听起来效率低下,但它实际上是两者中最直接且最不容易出错的。如果您需要等待所有线程,请使用以下循环:
std::set<HANDLE> thread_handles; // contains the handle of each worker thread
while (!thread_handles.empty()) {
std::set<HANDLE> threads_left;
for (std::set<HANDLE>::iterator cur_thread=thread_handles.begin(),
last=thread_handles.end();
cur_thread != last; ++cur_thread)
{
DWORD rc = ::WaitForSingleObject(*cur_thread, some_timeout);
if (rc == WAIT_OBJECT_0) {
::CloseHandle(*cur_thread); // necessary with _beginthreadex
} else if (rc == WAIT_TIMEOUT) {
threads_left.add(cur_thread); // wait again
} else {
// this shouldn't happen... try to close the handle and hope
// for the best!
::CloseHandle(*cur_thread); // necessary with _beginthreadex
}
}
std::swap(threads_left, thread_handles);
}
使用WaitForMultipleObjects 等待线程完成比听起来要困难一些。以下将等待所有线程;但是,它一次只等待WAIT_MAXIMUM_OBJECTS 线程。另一种选择是循环遍历每页线程。我会把这个练习留给读者;)
DWORD large_timeout = (5 * 60 * 1000); // five minutes
std::set<HANDLE> thread_handles; // contains the handle of each worker thread
std::vector<HANDLE> ary; // WaitForMultipleObjects wants an array...
while (!thread_handles.empty()) {
ary.assign(thread_handles.begin(), thread_handles.end());
DWORD rc = ::WaitForMultipleObjects(std::min(ary.size(), WAIT_MAXIMUM_OBJECTS),
&ary[0], FALSE, large_timeout);
if (rc == WAIT_FAILED) {
// handle a failure case... this is usually something pretty bad
break;
} else if (rc == WAIT_TIMEOUT) {
// no thread exited in five minutes... this can be tricky since one of
// the threads beyond the first WAIT_MAXIMUM_OBJECTS may have terminated
} else {
long idx = (rc - WAIT_OBJECT_0);
if (idx > 0 && idx < ary.size()) {
// the object at `idx` was signaled, this means that the
// thread has terminated.
thread_handles.erase(ary[idx]);
::CloseHandle(ary[idx]); // necessary with _beginthreadex
}
}
}
这不是很漂亮,但应该可以。如果您相信您的所有线程都会退出并且不介意等待它们,那么您可以使用WaitForMultipleObjects(ary.size(), &ary[0], TRUE, INFINITE)。这通常不是很安全,因为一个失控的线程会导致您的应用程序无限期阻塞并且只有当ary.size() 小于MAXIMUM_WAIT_OBJECTS 时它才会起作用。
当然,另一种选择是找到一个线程池实现并使用它来代替。编写线程代码并不是很有趣,尤其是当你必须在野外支持它时。考虑改用boost::thread_group 之类的东西。
【讨论】:
您可以使用 boost::thread 对象。在对象上调用join,它将等待线程完成。
【讨论】:
Windows 为一个线程提供事件以通知另一个线程。开箱即用的 Visual C++ 仅在 MFC 内提供对事件的支持。对于可移植的非 MFC 版本,请查看 Boost 库的 thread management classes。它们使启动和等待线程变得更加容易,尽管它们不提供对所有 Windows API 功能的直接访问。
【讨论】: