Уведомить об отделенной теме в DLL_DETACHED - PullRequest
0 голосов
/ 10 января 2020

У меня есть следующий код, который создает группу потоков и немедленно их отсоединяет:

#include <mutex>
#include <thread>
#include <queue>
#include <atomic>
#include <functional>

class TGroup final
{
private:
    std::mutex mutex;
    std::condition_variable condition;
    std::queue<std::function<void()>> tasks;
    std::atomic_bool stop;

public:
    TGroup();
    ~TGroup();
    TGroup(const TGroup&) = delete;
    TGroup& operator = (const TGroup&) = delete;

    void terminate();
    void execute(std::function<void()> &&task);
};

TGroup::TGroup() : mutex(), condition(), tasks(), stop(false)
{
    for (std::size_t i = 0; i < std::thread::hardware_concurrency(); ++i)
    {
        std::thread([this] {
            while(true)
            {
                std::unique_lock<std::mutex> lock(this->mutex);
                condition.wait(lock, [this] {
                    return !this->tasks.empty() || this->stop;
                });

                if (this->stop)
                {
                    lock.unlock();
                    break;
                }

                auto task = std::move(this->tasks.front());
                this->tasks.pop();
                lock.unlock();

                task();
            }
        }).detach();
    }
}

TGroup::~TGroup()
{
    this->terminate();
}

void TGroup::terminate()
{
    if (!this->stop)
    {
        std::unique_lock<std::mutex> lock(this->mutex);
        this->stop = true;
        lock.unlock();
        this->condition.notify_all();  //Causes a crash on Windows but not MacOS or Linux..
    }
}

void TGroup::execute(std::function<void()> &&task)
{
    if (!this->stop)
    {
        std::unique_lock<std::mutex> lock(this->mutex);
        this->tasks.emplace(task);
        lock.unlock();
        this->condition.notify_one();
    }
}

В моем DLL_DETACHED внутри DLLMain я вызываю group.terminate(). Он прекрасно работает, если я закомментирую this->condition.notify_all(); в TGroup.terminate().

. Есть ли причина, по которой уведомление условной переменной может привести к тупику? В настоящее время мой процесс НЕ завершается, когда я нажимаю кнопку выхода (мне нужно использовать TaskManager, чтобы убить его).

Есть идеи?

1 Ответ

1 голос
/ 11 января 2020

Ваша программа содержит гонку данных на *this между уничтожаемым объектом TGroup и пробужденным потоком, пытающимся получить доступ к this->tasks и другим переменным-членам. Поэтому он демонстрирует неопределенное поведение.

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

...