В своей программе мне необходимо реализовать остановку выполнения потоков в опр. момент, причём в любом случае.
Я так понял, необходимо сделать примерно так:
HANDLE handle_thread, hStopEvent;
void thread (void *args)
{
do
{
...
} while (WaitForSingleObject (hStopEvent, 0) != WAIT_OBJECT_0); // Проверяем, не установлено ли событие
}
int main (int argc, char *argv[])
{
hStopEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // Создаём событие для того, чтобы потом можно было завершить поток, если всё пойдёт, как надо
handle_thread = reinterpret_cast <HANDLE> (_beginthread (thread, 0, NULL)); // При помощи функции _beginthread создаём поток
... // Создаём ещё неск. потоков
SetEvent (hStopEvent); // Устанавливаем событиеif (WaitForSingleObject (handle_thread, 500) != WAIT_OBJECT_0) // Ждём, пока поток завершится
TerminateThread (handle_thread, (DWORD)-1); // В идеале даная строка вызваться, разумеется, не должна
CloseHandle (handle_thread); // Закрываем хэндл, чтобы освободить память, которая была выделена для потока
... // Закрываем ещё неск. потоков
CloseHandle (hStopEvent); // Закрываем хэндл события, которое мы использовали для завершения потоков только чтоreturn 0;
}
Что тут не так? Почему при таком коде у меня всё равно остаются утечки памяти?
Здравствуйте, YourLastSong, Вы писали:
YLS>В своей программе мне необходимо реализовать остановку выполнения потоков в опр. момент, причём в любом случае.
YLS>Я так понял, необходимо сделать примерно так:
YLS>
YLS>HANDLE handle_thread, hStopEvent;
YLS>void thread (void *args)
YLS>{
YLS>do
YLS>{
YLS>...
YLS>} while (WaitForSingleObject (hStopEvent, 0) != WAIT_OBJECT_0); // Проверяем, не установлено ли событие
YLS>}
YLS>int main (int argc, char *argv[])
YLS>{
YLS>hStopEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // Создаём событие для того, чтобы потом можно было завершить поток, если всё пойдёт, как надо
YLS>handle_thread = reinterpret_cast <HANDLE> (_beginthread (thread, 0, NULL)); // При помощи функции _beginthread создаём поток
YLS>... // Создаём ещё неск. потоков
YLS>SetEvent (hStopEvent); // Устанавливаем событие
YLS>if (WaitForSingleObject (handle_thread, 500) != WAIT_OBJECT_0) // Ждём, пока поток завершится
YLS>TerminateThread (handle_thread, (DWORD)-1); // В идеале даная строка вызваться, разумеется, не должна
YLS>CloseHandle (handle_thread); // Закрываем хэндл, чтобы освободить память, которая была выделена для потока
YLS>... // Закрываем ещё неск. потоков
YLS>CloseHandle (hStopEvent); // Закрываем хэндл события, которое мы использовали для завершения потоков только что
YLS>return 0;
YLS>}
YLS>
YLS>Что тут не так? Почему при таком коде у меня всё равно остаются утечки памяти?
Во-первых, использовать событие не обязательно (и неэффективно).
Достаточно глобальной флаговой переменной. Только не забудьте снабдить ее ключевым словом volatile,
иначе возможны неприятные побочные эффекты агрессивной оптимизации.
Во-вторых, такое завершение потока не вызывает деструкторы объектов, которые находятся в
области действия функции, и об этом явно сказано в MSDN.
К примеру, есть такой вот класс:
namespace// Чтобы не возникло конфликта имен ThreadProc и ThreadProc_Impl.
{
// Назначение этой функции заключается в паре дополнительных фигурных скобок,
// которые обеспечивают вызов деструкторов при выходе.void
_stdcall
ThreadProc_Impl(
__in VOID * pParam
)
{
some_class SomeClass;
}
// Точка входа в поток.unsigned int
_stdcall
ThreadProc(
__in VOID * pParam
)
{
ThreadProc_Impl(pParam);
_endthreadex(0);
return 0;
}
} // namespace
// ...
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
O>// Точка входа в поток.
O>unsigned int
O>_stdcall
O>ThreadProc(
O> __in VOID * pParam
O> )
O>{
O> ThreadProc_Impl(pParam);
O> _endthreadex(0);
O> return 0;
O>}
Смысла в этой функции — ровным счётом никакого. В функции потока не нужно явно вызывать _endthreadex — достаточно вернуть управление естественным образом.
O>Во-первых, использовать событие не обязательно (и неэффективно).
Почему?
O>Во-вторых, такое завершение потока не вызывает деструкторы объектов, которые находятся в O>области действия функции, и об этом явно сказано в MSDN.
При каком именно завершении потока? При помощи return или как?
Зачем использовать функцию _endthread явно вообще? Ведь при выходе из функции потока функция _endthread должна вызваться самостоятельно и освободить память, разве нет?
Здравствуйте, YourLastSong, Вы писали:
YLS>В своей программе мне необходимо реализовать остановку выполнения потоков в опр. момент, причём в любом случае.
Этого нельзя делать. Вы остановите поток в тот момент, когда он занимает какой-нибудь ресурс, критически необходимый всей программе. Например, кучу или же OS Loader Lock. Ресурс останется занятым, и программа не сможет сделать больше ничего полезного, кроме как завершиться.
Здравствуйте, Centaur, Вы писали:
C>Смысла в этой функции — ровным счётом никакого. В функции потока не нужно явно вызывать _endthreadex — достаточно вернуть управление естественным образом.
Польза такого оформления именно смысловая, чем физическая, так как _endthread(ex) все равно будет вызвана.
А явный вызов ее в ThreadProc указывает на то, какой функцией поток создавался, — CreateThread или
_beginthread(ex), — и какие тонкости из этого вытекают. У _beginthread есть такая особенность — если
функция потока выполнится за короткое время, вызывающему будет возвращен неверный код. И по этой же
причине wait-функции могут некорректно работать с дескрипторами потоков, созданных при помощи beginthread.
Здравствуйте, YourLastSong, Вы писали:
O>>Во-первых, использовать событие не обязательно (и неэффективно).
YLS>Почему?
Потому что wait-функции — это накладно.
А чтение значения переменной обойдется в несколько тактов процессора.
O>>Во-вторых, такое завершение потока не вызывает деструкторы объектов, которые находятся в O>>области действия функции, и об этом явно сказано в MSDN.
YLS>При каком именно завершении потока? При помощи return или как?
Я о TerminateThread. Деструкторы объектов C++ не будут вызваны.
YLS>Зачем использовать функцию _endthread явно вообще? Ведь при выходе из функции потока функция _endthread должна вызваться самостоятельно и освободить память, разве нет?
Вместо _beginthread/_endthread лучше использовать _beginthreadex/_endthreadex.
О причинах подробно написано в MSDN.
YLS>Сделал примерно так:
YLS>
Невинный код, таящий неочевидную, хоть и очень маловероятную, ошибку.
Дело в том, что дескриптор потока, созданного _beginthread, автоматически закрывается при
уничтожении потока. Это значит, что если после строчки "thread_flag = true" поток завершит
свою работу до вызова функции WaitForSingleObject, она будет работать с уже невалидным дескриптором.
Который, кстати, система может назначить другому потоку. А там и TerminateThread...
Короче, _beginthread/_endthread — ненадежно, надо юзать их аналоги _beginthreadex/_endthreadex,
там таких проблем нету.
O>Невинный код, таящий неочевидную, хоть и очень маловероятную, ошибку. O>Дело в том, что дескриптор потока, созданного _beginthread, автоматически закрывается при O>уничтожении потока. Это значит, что если после строчки "thread_flag = true" поток завершит O>свою работу до вызова функции WaitForSingleObject, она будет работать с уже невалидным дескриптором. O>Который, кстати, система может назначить другому потоку. А там и TerminateThread... O>Короче, _beginthread/_endthread — ненадежно, надо юзать их аналоги _beginthreadex/_endthreadex, O>там таких проблем нету.
А если использовать CreateThread таким же образом?
Здравствуйте, YourLastSong, Вы писали:
YLS>А если использовать CreateThread таким же образом? YLS>... YLS>Так тоже будет прав. или всё же нет вообще?
Так нормально. Только CreateThread ведет к утечкам памяти. Очень небольшим, но это морально неприятно.
Все же _beginthreadex/_endthreadex — лучший выбор из возможных вариантов.
1) TerminateThread срабатывает? Если да, то утечки будут, даже если функция потока вообще ничего не делает — при TerminateThread не освобождается стек потока.
2) Что за утечки, какой памяти, как Вы их определяете?
3) Если временно удалить вызов TerminateThread, проблема исчезает?
ты уже прочитал это? я спрашиваю потому, что если бы да, такого вопроса не было.
касательно остановки потоков могу сказать следующее. TerminateThread должна вызываться только при завершении приложения. в другом случае возможен только метод зомби — если срабатывает тайм-аут, ты ставишь флаг, что поток не нужен. поток, перед определенными действиями, проверяет флаг и в случае не нужности завершается сам.
Здравствуйте, о_О, Вы писали:
о_О>Здравствуйте, YourLastSong, Вы писали:
о_О>ты уже прочитал это? я спрашиваю потому, что если бы да, такого вопроса не было. о_О>касательно остановки потоков могу сказать следующее. TerminateThread должна вызываться только при завершении приложения. в другом случае возможен только метод зомби — если срабатывает тайм-аут, ты ставишь флаг, что поток не нужен. поток, перед определенными действиями, проверяет флаг и в случае не нужности завершается сам.
Здравствуйте, о_О, Вы писали:
о_О>Здравствуйте, о_О, Вы писали:
о_О>>Здравствуйте, YourLastSong, Вы писали:
о_О>>ты уже прочитал это? я спрашиваю потому, что если бы да, такого вопроса не было. о_О>>касательно остановки потоков могу сказать следующее. TerminateThread должна вызываться только при завершении приложения. в другом случае возможен только метод зомби — если срабатывает тайм-аут, ты ставишь флаг, что поток не нужен. поток, перед определенными действиями, проверяет флаг и в случае не нужности завершается сам.
о_О>ссылка выпилилась http://www.ozon.ru/context/detail/id/116668/
JR>Что за утечки, какой памяти, как Вы их определяете?
Смотрю через диспетчер задач.
Например, через диспетчер задач видно, что память не освобождается при выходе из потока.
JR>Если временно удалить вызов TerminateThread, проблема исчезает?