Як зупинити потік win32, який блокує?

Я створив спеціальний ThreadPool , який запускає декілька потоків win32 за допомогою _beginthreadex() . У потоках використовується простий цикл, який намагається видалити завдання з блокування черги, але іноді мені потрібно зупинити потоки, і якщо вони заблоковані в Dequeue , то я не знаю, як отримати нитки з цього блокування стану.

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) двічі)? Крім того, чи є кращий підхід, ніж це?

5

2 Відповіді

Виклик _endthreadex (0) в кінці методу потоку в порядку. Це також необов'язково. Якщо ви просто залиште метод потоку зазвичай, то вам потрібен код _endthreadex (0) .

Ви можете викликати _endthread або _endthreadex явно, щоб закінчити потоку; проте, _endthread або _endthreadex називається автоматично, коли нитка повертається з переданої процедури як параметр до _beginthread або _beginthreadex. ref

Відправлення завдання завершення - правильний спосіб отримати блокований потік потоку потоку для розблокування та завершення роботи.

Отже, узагальнюючи:

  1. Ваша стратегія хороша, і виконання TerminationTask :: Run правильне.
  2. Ви можете видалити нешкідливий виклик до _endthreadex (0) в кінці ThreadPool :: Loop .
6
додано
Ах, отримай це ... Я зніму непотрібний _endthreadex (0) ! Дякую за підтвердження!
додано Автор Kiril, джерело
Мені потрібен прапорець _running , якщо у мене є кілька завдань у черзі, і я хочу закінчити роботу, перш ніж обробляти всі завдання. Завдання завершення роботи корисно лише тоді, коли у мене немає завдань у черзі, і я хочу вийти з блокування стану. Іншими словами: я можу мати ThreadPool :: Terminate (який дозволяє обробляти всі завдання, просто додавши TerminateTask до кінця блокування черги і відключивши enqueuing), і я міг би мати метод ThreadPool :: TerminateNow , який просто зупиняє обробку завдань незалежно від того, що знаходиться в черзі.
додано Автор Kiril, джерело
@JohnDibling Ви можете пояснити, як це зробити? Ваш коментар змусив мене подумати про те, що завдання Runnable має якийсь код блокування ... Я не контролюю це, так що це буде хороша політика для переривання Runnable теж? Може бути, я повинен запровадити певну політику для Runnable , щоб вона мала метод "переривання", який виводить його з блокування стану (що відповідає особі, яка реалізує інтерфейс, щоб гарантувати це)
додано Автор Kiril, джерело
@David, я чомусь прийшов до висновку, що Windows ThreadPool було недостатньо певним чином, тому я вирішив написати свій ThreadPool , що імітує поведінку Java ThreadPool (мені подобається, як це працює в Java). Однак я знову подивимося на winapi, і я спробую побачити, чи дійсно я мав вагому причину не використовувати winapi ThreadPool .
додано Автор Kiril, джерело
+1, але інші варіанти пари можуть бути: 1) встановити "смертну подію" спеціально для пробудження потоку, заблокованого в WaitForMultipleObjects , або 2) надіслати "смертельну роботу" через QueueUserAPC
додано Автор John Dibling, джерело
На відміну від вас потрібен цикл while (_running) ? Кожного разу, коли я це зробив, я написав while (true) , а потім надіслав завдання завершення, щоб припинити процедуру. Оскільки у вас є механізм припинення, начебто невірно мати ще один значок у прапорі _running . Дійсно, я підозрюю, що зроблено таким чином, що ви кодуєте, буде необов'язково складним і несете потенціал для умов перегонів.
додано Автор David Heffernan, джерело
ОК, я розумію!
додано Автор David Heffernan, джерело
Дуже важко реалізувати асинхронне скасування. Такі рамки, як .net, пропонують такий рівень функціональності. Інша думка, чи не стандартний пул потоків у Windows, що вам потрібно. Ви все-таки позначили питання winapi.
додано Автор David Heffernan, джерело

Встановлення тематичного завдання в черзі є правильним. Хотів би спробувати інший підхід до вирішення цього питання:

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
        }
    }
} 
0
додано
IT KPI C/С++ новым годом
IT KPI C/С++ новым годом
747 учасників

Чат обсуждения С/С++. - Вопросы "напишите за меня лабу" - это оффтоп. - Оффтоп, флуд, оскорбления и вбросы здесь не приняты. - За нарушение - предупреждение или mute на неделю. - За спам и рекламу - ban. Все чаты IT KPI: https://t.me/itkpi/1147