【问题标题】:Correct way of checking if threads are done?检查线程是否完成的正确方法?
【发布时间】:2011-03-11 22:40:44
【问题描述】:

我正在使用 _beginthread 在我的应用程序中使用多线程,现在要等到所有线程都完成我有全局布尔值,当每个线程完成时我都设置为 true,所以在那之前我处于一个 while 循环中。必须有更清洁的方法吗?

谢谢

【问题讨论】:

    标签: c++ c windows multithreading


    【解决方案1】:

    您可以使用WaitForMultipleObjects 等待主线程中的线程完成。

    【讨论】:

    • 如果你只有一个线程ID,你需要先调用OpenThread把它转换成一个HANDLE。
    • 不,不要调用 OpenThread!如果线程已经退出并且重新分配了 ID,那么您最终可能会得到另一个随机线程的句柄。使用 _beginthreadex,它返回线程的句柄。 (_beginthread 也返回一个句柄,但是(引用文档)它“可能无效或指向另一个线程”。切勿使用 _beginthread。)
    【解决方案2】:

    您想了解的是线程同步技术——幸运的是,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

    【讨论】:

    • 你肯定需要使用事件而不是布尔值。
    【解决方案3】:

    请改用_beginthreadex_beginthread_beginthreadex return a thread handle 都有,但是以_beginthread 开头的线程在完成时会自动关闭其句柄,因此使用它进行同步并不可靠。

    线程句柄可以与Win32的其中一种同步函数一起使用,例如WaitForSingleObjectWaitForMultipleObjects

    完成后,_beginthreadex 返回的句柄必须用CloseHandle() 关闭。

    【讨论】:

      【解决方案4】:

      通常的方法是保留所有线程句柄,然后等待每个句柄。当句柄发出信号时,线程已完成,因此从线程集中移除。我使用std::set<HANDLE> 来跟踪线程句柄。在 Windows 中等待多个对象有两种不同的方法:

      1. 遍历集合并调用WaitForSingleObject,每个都超时
      2. 将集合转换为数组或向量并调用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(), &amp;ary[0], TRUE, INFINITE)。这通常不是很安全,因为一个失控的线程会导致您的应用程序无限期阻塞并且只有当ary.size() 小于MAXIMUM_WAIT_OBJECTS 时它才会起作用。

      当然,另一种选择是找到一个线程池实现并使用它来代替。编写线程代码并不是很有趣,尤其是当你必须在野外支持它时。考虑改用boost::thread_group 之类的东西。

      【讨论】:

        【解决方案5】:

        您可以使用 boost::thread 对象。在对象上调用join,它将等待线程完成。

        【讨论】:

          【解决方案6】:

          Windows 为一个线程提供事件以通知另一个线程。开箱即用的 Visual C++ 仅在 MFC 内提供对事件的支持。对于可移植的非 MFC 版本,请查看 Boost 库的 thread management classes。它们使启动和等待线程变得更加容易,尽管它们不提供对所有 Windows API 功能的直接访问。

          【讨论】:

            猜你喜欢
            • 2012-07-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-15
            • 1970-01-01
            • 2015-02-18
            • 2019-02-16
            • 1970-01-01
            相关资源
            最近更新 更多