ThreadPool, QueueUserWorkItem и тупик при завершении работы - PullRequest
2 голосов
/ 01 апреля 2009

Я только что реализовал пул потоков, как описано здесь

Аллен Бауэр на пулах потоков

Очень простая реализация, работает нормально, но мое приложение больше не закрывается. Кажется, что два рабочих потока (и еще один поток, я думаю, поток очередей) застряли в функции

ntdll.ZwRemoveIoCompletion

Я помню, что читал что-то о завершениях ввода-вывода в записи справки для QueueUserWorkItem (функция WinAPI, используемая в реализации пула потоков), но я не мог понять это правильно. Я использовал WT_EXECUTELONGFUNCTION для моих рабочих потоков, так как выполнение может занять некоторое время, и я хочу, чтобы новый рабочий поток был создан вместо ожидания завершения существующих. Некоторые задачи, назначенные рабочим потокам, выполняют некоторые операции ввода-вывода. Я пытался использовать WT_EXECUTEINIOTHREAD, но, похоже, это не помогло.

Следует отметить, что основной поток ожидает входа в критическую секцию, причем стек вызовов равен

.
System.Halt0, System.FinalizeUnits, Classes.Finalization, TThread.Destroy,
RtlEnterCriticalSection, RtlpWaitForCriticalSection

Есть идеи, что я здесь делаю не так? Заранее спасибо за помощь.

Ответы [ 3 ]

0 голосов
/ 02 апреля 2009

Вы должны выйти из критической секции, прежде чем сможете войти снова. Так что проблема в замке.

В какой-то теме:

EnterCriticalSection(SomeCriticalSection);
sort code...
LeaveCriticalSection(SomeCriticalSection);

В какой-то другой теме:

EnterCriticalSection(SomeCriticalSection);
clean up code...
LeaveCriticalSection(SomeCriticalSection);

Если код сортировки выполняется в первом потоке, а второй поток пытается запустить код очистки, второй поток будет ждать, пока код сортировки завершится, и вы выйдете из критической секции. Только после выхода из критической секции вы можете войти в ту же критическую секцию. Я надеюсь, что это поможет вам сузить код взаимоблокировки, потому что он находится внутри критической секции.

Чтобы получить дескриптор порта завершения, вы можете сохранить его при создании порта завершения:

FIoCPHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0 , 0, FNumberOfConcurrentThreads);
0 голосов
/ 11 декабря 2009

При использовании QueueUserWorkItem, пока рабочие потоки возвращены в пул потоков, вам не нужно ничего делать, чтобы их отключить. Компонент WT_EXECUTEDEFAULT пула потоков ставит рабочие элементы в очередь на порт завершения ввода / вывода. Этот порт является частью внутренней реализации пула потоков и недоступен для вас.

Не могли бы вы предоставить более подробные стеки вызовов для застрявших потоков? Это значительно упростит диагностику этой проблемы.

0 голосов
/ 01 апреля 2009

Чтобы убедиться, что рабочие потоки закрыты, вам нужно как-то разбудить их, если они ожидают на пустом порту завершения ввода-вывода. Казалось бы, самый простой способ - отправить какое-либо сообщение NULL в порт - тогда они должны рассматривать это как сигнал для упорядоченной остановки.

...