繁体   English   中英

在 C++ 中使用 std::thread 和 std::condition_variable 自定义后台任务的 SIGABRT

[英]SIGABRT on custom background task using std::thread and std::condition_variable in C++

我编写了一个小的async_task类,它使线程保持温暖,以便我可以执行后台计算。 任务可以由多个线程触发,但在任一时刻只能运行一个任务实例。 在我的其中一台 CI 服务器(大约 2011 年的非常旧且速度缓慢的 mac mini - intel penryn 处理器)上,我的单​​元测试有时会因 SIGABRT 失败(在 macOS 10.13 上使用 Clang 9.0 - 而不是 AppleClang 编译)。 它在 Windows 10 构建机器上从未失败过 - 英特尔 i9 处理器。

以下是被测代码和提取到独立 C++ 应用程序中的单元测试的最小表示:

#include <thread>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <functional>
#include <iostream>

// class under test...

class async_task final
{
    std::thread thread_;
    std::condition_variable run_request_;
    std::mutex run_request_mutex_;
    std::atomic<bool> quit_{ false };
    std::atomic<bool> run_{ false };

public:
    async_task(std::function<void()> task) :
        thread_{ [this, task] { thread_proc(task); }}
    {
    }

    ~async_task()
    {
        {
            std::unique_lock<std::mutex> lock(run_request_mutex_);

            quit_ = true;
        }

        run_request_.notify_one();

        thread_.join();
    }

    void run()
    {
        {
            std::unique_lock<std::mutex> lock(run_request_mutex_);

            run_ = true;
        }

        run_request_.notify_one();
    }

private:
    void thread_proc(std::function<void()> task)
    {
        while (!quit_)
        {
            {
                std::unique_lock<std::mutex> lock{ run_request_mutex_ };

                run_request_.wait(lock, [this] { return quit_ || run_; });
            }

            bool run = false;

            if (run_.exchange(false))
            {
                run = !quit_;
            }

            if (run)
            {
                task();
            }
        }
    }
};


// exercising code...

int main()
{
    std::condition_variable condition;
    std::mutex mutex;

    std::atomic<bool> value = false;

    async_task task{ [&value, &mutex, &condition]()
    {
        {
            std::unique_lock<std::mutex> lock(mutex);

            value = true;
        }

        condition.notify_one();
    } };

    task.run();

    {
        using namespace std::chrono_literals;

        std::unique_lock<std::mutex> lock(mutex);

        if (!value)
        {
            condition.wait_for(lock, 5s, [&value] { return value.load(); });
        }
    }

    return EXIT_SUCCESS;
}

肯定存在竞争条件,但我终其一生都无法发现可能导致 SIGABRT 的原因。 任何人都可以发现问题吗?

更新:将互斥锁添加到析构函数以保护quit_因为这已被指出为次要问题 - 尽管不是问题的原因。

明显的竞争是成员初始化的顺序: std::thread立即启动,生成的线程可以在实际构造之前访问互斥锁和条件变量。 使std::thread成为类的最后一个成员应该可以解决这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM