Правильный способ проверки, если потоки сделаны? - PullRequest
6 голосов
/ 05 июля 2010

Я использую многопоточность в своем приложении с _beginthread и сейчас, чтобы дождаться завершения всех потоков, у меня есть глобальные bool, которые устанавливаются в true, когда каждый поток завершается, так что я до тех пор в цикле while.Должен быть более чистый способ сделать это?

Спасибо

Ответы [ 6 ]

8 голосов
/ 05 июля 2010

Вы можете использовать WaitForMultipleObjects для ожидания завершения потоков в первичном потоке.

3 голосов
/ 05 июля 2010

То, на что вы хотите взглянуть, - это методы синхронизации потоков - к счастью, в 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

2 голосов
/ 05 июля 2010

Обычный метод состоит в том, чтобы сохранить все дескрипторы потоков, а затем ждать каждого дескриптора. Когда дескриптор сигнализирует , поток завершен, поэтому он удаляется из набора потоков. Я использую 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(), &ary[0], TRUE, INFINITE). Это, как правило, не очень безопасно, так как бегущая нить приведет к тому, что ваше приложение будет блокироваться на неопределенный срок и , оно будет работать только если ary.size() меньше MAXIMUM_WAIT_OBJECTS.

Конечно, другой вариант - найти реализацию пула потоков и использовать ее вместо этого. Написание многопоточного кода на самом деле не очень весело, особенно если вам нужно поддерживать его на свободе. Попробуйте вместо этого использовать что-то вроде boost::thread_group.

2 голосов
/ 05 июля 2010

Используйте _beginthreadex вместо. И _beginthread, и _beginthreadex возвращают дескриптор потока , но поток, начинающийся с _beginthread, автоматически закрывает свой дескриптор по завершении, поэтому использование его для синхронизации ненадежно.

Дескриптор потока может использоваться с одной из функций синхронизации Win32, такой как WaitForSingleObject или WaitForMultipleObjects .

Когда все сделано, дескрипторы, возвращаемые _beginthreadex, должны быть закрыты с помощью CloseHandle().

1 голос
/ 06 июля 2010

Вы можете использовать объекты boost :: thread. Вызовите join для объекта, и он будет ждать окончания потока.

0 голосов
/ 05 июля 2010

Windows предоставляет события для одного потока, чтобы уведомить другой.Из коробки Visual C ++ обеспечивает поддержку событий только внутри MFC.Для переносной версии без MFC проверьте классы управления потоками библиотеки Boost.Они значительно облегчают запуск и ожидание потоков, хотя и не обеспечивают прямой доступ ко всем функциям Windows API.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...