2010-07-05 5 views
5

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

Благодаря

ответ

8

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

+0

Если у вас есть только идентификатор потока, вам нужно будет сначала преобразовать его в HANDLE, вызвав [OpenThread] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335 (v = vs .85) .aspx). – rustyx

+2

Нет, не называйте OpenThread! Если поток уже вышел и идентификатор был переназначен, вы могли бы получить дескриптор для другого случайного потока. Используйте _beginthreadex, который возвращает дескриптор потока. (_beginthread также возвращает дескриптор, но (цитируя документы) он «может быть недействительным или указывать на другой поток». Никогда не используйте _beginthread.) – benrg

3

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

+0

Вам определенно нужно использовать события над bools. – DanDan

2

Вместо этого используйте _beginthreadex. И _beginthread, и _beginthreadexreturn a thread handle, но поток, начатый с _beginthread, автоматически закрывает свой дескриптор, когда он заканчивается, поэтому его использование для синхронизации не является надежным.

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

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

0

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

1

Обычный способ состоит в том, чтобы сохранить все ручки потока и затем ждать каждого дескриптора. Когда ручка сигнализирована, нить закончила так, что она удалена из набора нитей. Я использую 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.

0

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