std :: atomic_flag停止多个线程

时间:2016-02-27 12:32:47

标签: c++ multithreading c++11 atomic

我试图使用std::atomic_flag停止多个工作线程。从Issue using std::atomic_flag with worker thread开始,以下作品:

#include <iostream>
#include <atomic>
#include <chrono>
#include <thread>

std::atomic_flag continueFlag;
std::thread t;

void work()
{
    while (continueFlag.test_and_set(std::memory_order_relaxed)) {
        std::cout << "work ";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void start()
{
    continueFlag.test_and_set(std::memory_order_relaxed);
    t = std::thread(&work);
}

void stop()
{
    continueFlag.clear(std::memory_order_relaxed);
    t.join();
}

int main()
{
    std::cout << "Start" << std::endl;
    start();
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << "Stop" << std::endl;
    stop();
    std::cout << "Stopped." << std::endl;

    return 0;
}

尝试重写为多个工作线程:

#include <iostream>
#include <atomic>
#include <chrono>
#include <thread>
#include <vector>
#include <memory>

struct thread_data {
    std::atomic_flag continueFlag;
    std::thread thread;
};

std::vector<thread_data> threads;

void work(int threadNum, std::atomic_flag &continueFlag)
{
    while (continueFlag.test_and_set(std::memory_order_relaxed)) {
        std::cout << "work" << threadNum << " ";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void start()
{
    const unsigned int numThreads = 2;

    for (int i = 0; i < numThreads; i++) {
        ////////////////////////////////////////////////////////////////////
        //PROBLEM SECTOR
        ////////////////////////////////////////////////////////////////////
        thread_data td;
        td.continueFlag.test_and_set(std::memory_order_relaxed);

        td.thread = std::thread(&work, i, td.continueFlag);

        threads.push_back(std::move(td));
        ////////////////////////////////////////////////////////////////////
        //PROBLEM SECTOR
        ////////////////////////////////////////////////////////////////////
    }
}

void stop()
{
    //Flag stop
    for (auto &data : threads) {
        data.continueFlag.clear(std::memory_order_relaxed);
    }
    //Join
    for (auto &data : threads) {
        data.thread.join();
    }
    threads.clear();
}

int main()
{
    std::cout << "Start" << std::endl;
    start();
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << "Stop" << std::endl;
    stop();
    std::cout << "Stopped." << std::endl;

    return 0;
}

我的问题是&#34;问题部门&#34;在上面。即创建线程。我无法解决如何实例化线程并将变量传递给工作线程的问题。

现在的错误是引用此行threads.push_back(std::move(td));,错误为Error C2280 'thread_data::thread_data(const thread_data &)': attempting to reference a deleted function

尝试使用这样的unique_ptr:

        auto td = std::make_unique<thread_data>();
        td->continueFlag.test_and_set(std::memory_order_relaxed);

        td->thread = std::thread(&work, i, td->continueFlag);

        threads.push_back(std::move(td));

在第std::atomic_flag::atomic_flag(const std::atomic_flag &)': attempting to reference a deleted function行给出错误td->thread = std::thread(&work, i, td->continueFlag);。我是否从根本上误解了std :: atomic_flag的使用?它真的既不可移动又不可复制?

2 个答案:

答案 0 :(得分:0)

你的第一种方法实际上更接近事实。问题是它将对本地for循环范围内的对象的引用传递给每个线程,作为参数。但是,当然,一旦循环迭代结束,该对象就会超出范围并被破坏,使每个线程都引用一个被破坏的对象,从而导致未定义的行为。

在创建线程后,没有人关心您将对象移动到std::vector这一事实。该线程接收了对本地范围对象的引用,并且知道了所有这些内容。故事结束。

首先将对象移动到向量中,然后传递给每个线程,std::vector中对象的引用也不起作用。一旦向量内部重新分配,作为其自然增长的一部分,您将处于相同的泡菜中。

在实际启动任何threads之前,需要首先创建整个std::thread数组。如果虔诚地遵循RAII原则,那只不过是对std::vector::resize()的简单调用。

然后,在第二个循环中,遍历完全烹饪的threads数组,然后为数组中的每个元素生成std::thread

答案 1 :(得分:0)

我用unique_ptr解决方案几乎。我只需要将调用作为std :: ref()传递:

std::vector<std::unique_ptr<thread_data>> threads;

void start()
{
    const unsigned int numThreads = 2;

    for (int i = 0; i < numThreads; i++) {
        auto td = std::make_unique<thread_data>();
        td->continueFlag.test_and_set(std::memory_order_relaxed);
        td->thread = std::thread(&work, i, std::ref(td->continueFlag));

        threads.push_back(std::move(td));
    }
}

然而,在上面的Sam的启发下,我也想出了一种非指针方式:

std::vector<thread_data> threads;

void start()
{
    const unsigned int numThreads = 2;

    //create new vector, resize doesn't work as it tries to assign/copy which atomic_flag
    //does not support
    threads = std::vector<thread_data>(numThreads);
    for (int i = 0; i < numThreads; i++) {
        auto& t = threads.at(i);
        t.continueFlag.test_and_set(std::memory_order_relaxed);
        t.thread = std::thread(&work, i, std::ref(t.continueFlag));
    }
}