Завершить потоки в пуле потоков PPL

Библиотека PPL Microsoft содержит мощные концепции распараллеливания и реализует их с помощью пула потоков, поэтому при выполнении задач PPL обычно не создаются новые потоки. Однако, похоже, не существует способа явно остановить потоки в пуле потоков.

Причина, по которой я хочу явно остановить потоки, связана с Qt. Некоторые методы Qt хранят информацию в выделенном экземпляре класса, а указатель на этот экземпляр класса хранится в локальном хранилище потока. Эта память очищается только в том случае, если потоки останавливаются корректно. В противном случае Qt не сможет очистить эту выделенную память.

Комбинация PPL с Qt подразумевает, что эта память не освобождается должным образом при выходе, что само по себе не является проблемой, но, к сожалению, наша библиотека распределения памяти сообщает об этой неосвобожденной памяти как об утечке памяти (см. Кто-нибудь использует valgrind и Qt? для решения аналогичной проблемы).

Мы заметили, что если мы сами создаем потоки (то есть не используя пул потоков PPL), об утечках не сообщается. Если мы используем PPL, об утечках сообщается.

Итак, вопрос: есть ли способ явно остановить потоки в пуле потоков PPL?


person Patrick    schedule 31.01.2019    source источник
comment
Вы пытались изменить планировщик (возможно, используя другие политики)? docs.microsoft.com/en- us / cpp / parallel / concrt / возможно, пул будет уничтожен при отключении планировщика. PPL, похоже, использует собственный пул с VS 2015: docs.microsoft.com/en-us/cpp/parallel/concrt/ В противном случае вы не сможете остановить какой-либо поток извне. Обычно вы вежливо просите поток остановиться (используя какое-то событие и т. Д.). Существует API-интерфейс TerminateThread, но обычно его не рекомендуется использовать (не рекомендуется).   -  person Simon Mourier    schedule 03.07.2020
comment
Кажется, что PPL не закрывает какие-либо ресурсы. Я попытался отследить время жизни объекта Concurrency::details::ThreadProxy (который содержит дескриптор потока Windows), и я не вижу, что вызывается деструктор этого класса. После выхода из функции main ОС просто убивает все потоки без очистки. Код библиотеки PPL не самого лучшего качества. Здесь не используются умные указатели.   -  person Dmytro Ovdiienko    schedule 09.07.2020


Ответы (1)


PPL следует той же концепции в C ++, что и асинхронное программирование в C #.

Общая идея - кооперативная отмена - вы можете попросить поток остановить, поток решает, когда можно отменить. Вы не должны убивать задачи / потоки, чтобы поток не был остановлен неопределенной инструкцией.

Здесь вы можете увидеть пример того, как остановить поток / задачу с помощью ppl:

include <ppltasks.h>
#include <concrt.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

bool do_work()
{
    // Simulate work.
    wcout << L"Performing work..." << endl;
    wait(250);
    return true;
}

int wmain()
{
    cancellation_token_source cts;
    auto token = cts.get_token(); // <-- allow early cancellation, therefore share a cancellation_token

    wcout << L"Creating task..." << endl;

    // Create a task that performs work until it is canceled.
    auto t = create_task([&]
    {
        bool moreToDo = true;
        while (moreToDo)
        {
            // Check for cancellation.
            if (token.is_canceled()) //<-- check for cancellation in the background thread
            {
                // Cancel the current task.
                cancel_current_task(); //<-- informs the caller, "hey I got it..."
            }
            else 
            {
                // Perform work.
                moreToDo = do_work();
            }
        }
    }, token);

    // Wait for one second and then cancel the task.
    wait(1000);

    wcout << L"Canceling task..." << endl;
    cts.cancel();

    // Wait for the task to cancel.
    wcout << L"Waiting for task to complete..." << endl;

    t.wait(); //<-- this is import

    wcout << L"Done." << endl;
}

Но чтобы помочь вам больше - вы можете предоставить нам исходный код?

person Bernd    schedule 05.07.2020
comment
Спасибо за Ваш ответ. К сожалению, это не решает основную проблему ситуации - локальные переменные потока. Ваш пример на самом деле не приводит к остановке потока. Поток будет убит завершением процесса и никогда не разрушит локальные переменные потока. Если вы хотите увидеть это в коде, объявите класс, который выводит сообщение в своем деструкторе, объявите локальный для потока экземпляр этого класса в do_work и убедитесь, что сообщение никогда не отображается. Попробуйте запустить этот Godbolt в MSVC: godbolt.org/z/DLecau - person Chronial; 06.07.2020
comment
MSVC, похоже, в настоящее время недоступен на Godbolt. Вам нужно дождаться завершения потока. Уничтожение локальной переменной происходит в самом конце. Эта функция автоматического ожидания интегрирована в std :: future. См. Cppreference: en.cppreference.com/w/cpp/thread/future/ % 7Efuture и en.cppreference.com/w/cpp/thread/async (Примечания) В PPL нужно явно ждать. - person Bernd; 06.07.2020
comment
Кстати, не стоит вызывать get () - иначе это просто накладные расходы ... - person Bernd; 06.07.2020
comment
Здесь вы можете увидеть образец: godbolt.org/z/3sysok - person Bernd; 06.07.2020
comment
Вам следует избегать прямого использования PPL - просто используйте std :: async и std :: future, если можете. В MSVC это реализовано через PPL под капотом. - person Bernd; 06.07.2020
comment
Я знаю, что msvc недоступен в Godbolt - вам придется попробовать это локально. Ваш код не будет выводить уничтожение C, если вы действительно запустите его под msvc. Это проблема, которую нужно решить. Вот онлайн-ссылка: rextester.com/UEJ92122 - person Chronial; 06.07.2020
comment
Спасибо за ясность. Теперь я понял вашу точку зрения. Вы не используете PPL напрямую, но вас не устраивает реализация MS для std :: async. См. Эту ссылку: stackoverflow.com/questions/50897768/. Это хорошо известная ошибка. Я проверю, есть ли обходной путь. Кстати: MSVC доступен по адресу godbolt, но иногда он показывает такие вещи, как Bad Gateway - person Bernd; 06.07.2020
comment
Было бы более понятно по этой ссылке: github.com/microsoft/STL/issues/949. - person Bernd; 06.07.2020
comment
msvc доступен только для компиляции на godbolt. Вы не можете запустить результат. Мне хорошо известно о несоответствии MSVC STL. Этот вопрос касается способа выключить пул процессов по умолчанию. Я не думаю, что это возможно, но, возможно, я что-то упускаю. - person Chronial; 06.07.2020
comment
Да, я думаю, что вам нужно очень глубоко углубиться в STL (я нашел некоторые части, но у них есть некоторые условия) ... Так что, похоже, не стоит (только для запроса на перенос) - поэтому для отладки вам следует используйте jthread или удалите те части, которые нуждаются в thread_local - person Bernd; 06.07.2020
comment
@BerndBaumanns, спасибо за ссылку. Это действительно проблема. PPL не останавливает потоки правильно, а просто позволяет ОС убивать их при выходе. Я бы действительно избавился от thread_local, если бы это был мой код, но часть кода внизу - это Qt, и я не хочу отклоняться от стандартного кода Qt. Мне, наверное, придется смириться с этой проблемой. - person Patrick; 13.07.2020