Я использую многопоточность в своем приложении с _beginthread и прямо сейчас ожидать, пока все потоки не сделаны, у меня есть глобальный bools, который установлен на истинный, поскольку каждый поток завершается так, я нахожусь в некоторое время цикле до тех пор. Должен быть более чистый способ сделать это?
Спасибо
Вы можете использовать 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
Обычный метод - сохранить все дескрипторы потоков, а затем ждать каждого дескриптора. Когда дескриптор сигнализирует , поток завершен, поэтому он удаляется из набора потоков. Я использую std :: set
, чтобы отслеживать дескрипторы потоков. В 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
.
Вместо этого используйте _beginthreadex
. И _beginthread
, и _beginthreadex
возвращают хэндл потока, но поток, запущенный с помощью _beginthread
, автоматически закрывает свой хэндл при завершении работы, поэтому использование его для синхронизации ненадежно.
Ручка потока может быть использована с одной из функций синхронизации Win32, таких как WaitForSingleObject или WaitForMultipleObjects.
После завершения, дескрипторы, возвращенные _beginthreadex
, должны быть закрыты с помощью CloseHandle()
.
Вы можете использовать объекты boost :: thread. Вызовите join
для объекта, и он будет ждать завершения потока.
Windows предоставляет события для уведомления одного потока другим. Из коробки Visual C++ обеспечивает поддержку событий только внутри MFC. Для переносимой версии, не относящейся к MFC, обратитесь к классам управления потоками библиотеки Boost. Они значительно упрощают запуск и ожидание потоков, хотя и не предоставляют прямого доступа ко всей функциональности Windows API.