简体   繁体   English

std :: atomic_flag停止多个线程

[英]std::atomic_flag to stop multiple threads

I'm trying to stop multiple worker threads using a std::atomic_flag . 我正在尝试使用std::atomic_flag停止多个工作线程。 Starting from Issue using std::atomic_flag with worker thread the following works: 问题开始, 使用带有工作线程std :: atomic_flag进行以下工作:

#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;
}

Trying to rewrite into multiple worker threads: 尝试重写为多个工作线程:

#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;
}

My issue is "Problem Sector" in above. 我的问题是上面的“问题部门”。 Namely creating the threads. 即创建线程。 I cannot wrap my head around how to instantiate the threads and passing the variables to the work thread. 我不能全神贯注于如何实例化线程并将变量传递给工作线程。

The error right now is referencing this line threads.push_back(std::move(td)); 现在的错误是引用此行threads.push_back(std::move(td)); with error Error C2280 'thread_data::thread_data(const thread_data &)': attempting to reference a deleted function . Error C2280 'thread_data::thread_data(const thread_data &)': attempting to reference a deleted function

Trying to use unique_ptr like this: 尝试像这样使用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));

Gives error std::atomic_flag::atomic_flag(const std::atomic_flag &)': attempting to reference a deleted function at line td->thread = std::thread(&work, i, td->continueFlag); 给出错误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::atomic_flag(const std::atomic_flag &)': attempting to reference a deleted function td->thread = std::thread(&work, i, td->continueFlag); . Am I fundamentally misunderstanding the use of std::atomic_flag? 我是否从根本上误解了std :: atomic_flag的使用? Is it really both immovable and uncopyable? 真的既不可移动又不可复制吗?

Your first approach was actually closer to the truth. 您的第一种方法实际上更接近事实。 The problem is that it passed a reference to an object within the local for loop scope to each thread, as a parameter. 问题在于,它将对本地for循环范围内的对象的引用作为参数传递给了每个线程。 But, of course, once the loop iteration ended, that object went out of scope and got destroyed, leaving each thread with a reference to a destroyed object, resulting in undefined behavior. 但是,当然,一旦循环迭代结束,该对象就会超出范围并被销毁,从而使每个线程都具有对销毁对象的引用,从而导致未定义的行为。

Nobody cared about the fact that you moved the object into the std::vector , after creating the thread. 没有人关心创建线程后将对象移动到std::vector的事实。 The thread received a reference to a locally-scoped object, and that's all it knew. 该线程收到了对本地作用域对象的引用,仅此而已。 End of story. 故事结局。

Moving the object into the vector first, and then passing to each thread a reference to the object in the std::vector will not work either. 首先将对象移动到向量中,然后将对std::vector对象的引用传递给每个线程也将不起作用。 As soon as the vector internally reallocates, as part of its natural growth, you'll be in the same pickle. 向量在内部重新分配后,作为其自然增长的一部分,您将处在相同的泡菜中。

What needs to happen is to have the entire threads array created first, before actually starting any std::thread s. 需要做的是先创建整个threads数组,然后再实际启动任何std::thread If the RAII principle is religiously followed, that means nothing more than a simple call to std::vector::resize() . 如果严格遵守RAII原则,则仅意味着对std::vector::resize()的简单调用。

Then, in a second loop, iterate over the fully-cooked threads array, and go and spawn off a std::thread for each element in the array. 然后,在第二个循环中,遍历完全煮熟的threads数组,然后为该数组中的每个元素生成一个std::thread

I was almost there with my unique_ptr solution. 我的unique_ptr解决方案几乎在那儿。 I just needed to pass the call as a std::ref() as such: 我只需要像这样将调用作为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));
    }
}

However, inspired by Sam above I also figured a non-pointer way: 但是,受以上所述的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));
    }
}

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

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