Как остановить поток win32, который блокирует? - PullRequest
5 голосов
/ 27 октября 2011

Я создал пользовательский ThreadPool, который запускает несколько потоков win32 с _beginthreadex().Потоки выполняют простой цикл, который пытается удалить задачи из очереди блокировки, но иногда мне нужно остановить потоки, и если они заблокированы на Dequeue, тогда я не знаю, как вывести потоки из этой блокировкиstate.

void ThreadPool::Loop()
{
    while(_running)
    {
        try
        {
            // Attempts to dequeue a task and run it
            _taskQueue.Dequeue()->Run();
        }
        catch(BlockingQueueTerminate&)
        {
            // Eat the exception and check the running flag
            continue;
        }
    }
}

Моя идея заключалась в том, чтобы поставить в очередь то же количество специальных задач (назовем их «задачами завершения»), сколько потоков в пуле, и каждая «задача завершения» будет вызывать _endthreadex(0) вПорядок выхода из потока.Если в очереди блокировки есть другие задачи, то мне будет все равно, потому что, как только я снимаю задачу с очереди, я ее запускаю и проверяю флаг _running, чтобы определить, нужно ли еще потоку удалять задачи из очереди..

void TerminationTask::Run()
{
    _endthreadex(0);
}

У меня есть несколько опасений по поводу этого подхода;в основном, если я обработал задачу без завершения и флаг _running установлен на false, то мой поток не будет вызывать _endthreadex(0) при выходе из цикла.Мне было интересно, смогу ли я вызвать _endthreadex(0) в конце цикла следующим образом:

void ThreadPool::Loop()
{
    while(_running)
    {
        try
        {
            // Attempts to dequeue a task and run it
            _taskQueue.Dequeue()->Run();
        }
        catch(BlockingQueueTerminate&)
        {
            // Eat the exception and check the running flag
            continue;
        }
    }
    _endthreadex(0);
}

Это вызовет конфликт с моим TerminationTask или поток выйдет из цикла сразу после выполнения TerminationTask::Run() (т.е. он не будет звонить _endthreadex(0) дважды)?Кроме того, есть ли лучший подход, чем этот?

Ответы [ 2 ]

6 голосов
/ 27 октября 2011

Вызов _endthreadex(0) в конце метода потока в порядке.Это также необязательно.Если вы просто оставляете метод потока в обычном режиме, то для вас вызывается _endthreadex(0).

Вы можете явно вызвать _endthread или _endthreadex для завершения потока;однако _endthread или _endthreadex вызывается автоматически, когда поток возвращается из подпрограммы, переданной в качестве параметра в _beginthread или _beginthreadex. ref

Отправка задачи завершенияправильный способ заставить заблокированный поток пула потоков разблокироваться и выйти.

Итак, подведем итог:

  1. Ваша стратегия хороша, а реализация TerminationTask::Run правильная.
  2. Вы можете удалить безобидный звонок на _endthreadex(0) в конце ThreadPool::Loop.
0 голосов
/ 28 октября 2011

Правильное размещение задачи в очереди.Я бы попробовал другой подход к этому, хотя:

class TerminateThreadNow {};

void TerminationTask::Run()
{
    throw TerminateThreadNow();
} 

void ThreadPool::Loop()
{
    while(_running)
    {
        try
        {
            // Attempts to dequeue a task and run it
            _taskQueue.Dequeue()->Run();
        }
        catch(TerminateThreadNow&)
        {
            _running = false;
        }
        catch(BlockingQueueTerminate&)
        {
            // Eat the exception and check the running flag
        }
    }
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...