Correct way of checking if threads are done?

前端 未结 6 736
一向
一向 2020-12-18 05:10

I\'m using multithreading in my application with _beginthread and right now to wait until all threads are done I have global bools that get set to true as each thread comple

6条回答
  •  爱一瞬间的悲伤
    2020-12-18 05:26

    The usual method is to keep all of the thread handles and then wait on each handle. When the handle is signaled, the thread has finished so it is removed from the set of threads. I use std::set to keep track of the thread handles. There are two different methods for waiting on multiple objects in Windows:

    1. Iterate over the set and call WaitForSingleObject with a timeout on each one
    2. Convert the set into an array or vector and call WaitForMultipleObjects

    The first sounds inefficient, but it is actually the most direct and least error prone of the two. If you need to wait for all of the threads, then use the following loop:

    std::set thread_handles; // contains the handle of each worker thread
    while (!thread_handles.empty()) {
        std::set threads_left;
        for (std::set::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);
    }
    

    Using WaitForMultipleObjects to wait for the threads to finish is a bit more difficult than it sounds. The following will wait for all of the threads; however, it only waits for WAIT_MAXIMUM_OBJECTS threads at a time. Another options is to loop over each page of threads. I'll leave that exercise to the reader ;)

    DWORD large_timeout = (5 * 60 * 1000); // five minutes
    std::set thread_handles; // contains the handle of each worker thread
    std::vector 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
            }
        }
    }
    

    This isn't exactly pretty but it should work. If you trust that all of your threads will exit and don't mind waiting for them, then you can use WaitForMultipleObjects(ary.size(), &ary[0], TRUE, INFINITE). This usually isn't very safe though since a runaway thread will cause your application to block indefinitely and it will only work if ary.size() is less than MAXIMUM_WAIT_OBJECTS.

    Of course the other option is to find a thread pool implementation and use it instead. Writing threading code is not really a lot of fun especially once you have to support it in the wild. Consider using something like boost::thread_group instead.

提交回复
热议问题