C++ std::停止多个线程的原子_标志

C++ std::停止多个线程的原子_标志,c++,multithreading,c++11,atomic,C++,Multithreading,C++11,Atomic,我正在尝试使用std::atomic_标志停止多个工作线程。从以下工作开始: #include <iostream> #include <atomic> #include <chrono> #include <thread> std::atomic_flag continueFlag; std::thread t; void work() { while (continueFlag.test_and_set(std::memory_or

我正在尝试使用
std::atomic_标志
停止多个工作线程。从以下工作开始:

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

给出错误
std::atomic_flag::atomic_flag(const std::atomic_flag&)':试图在第
td->thread=std::thread(&work,i,td->continueFlag)行引用已删除的函数。我是否从根本上误解了std::atomic_标志的使用?它真的既不可移动又不可复制吗?

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

创建线程后,没有人关心您将对象移动到
std::vector
。线程收到了对局部作用域对象的引用,这就是它所知道的。故事结束了

首先将对象移动到向量中,然后向每个线程传递一个对
std::vector
中对象的引用也将不起作用。一旦载体内部重新分配,作为其自然生长的一部分,你将陷入同样的困境

需要做的是首先创建整个
线程
数组,然后再实际启动任何
std::thread
s。如果严格遵循这一原则,那就意味着只需调用
std::vector::resize()


然后,在第二个循环中,对完全煮熟的
线程
数组进行迭代,并为数组中的每个元素生成一个
std::thread

我的
独特的ptr
解决方案几乎就在那里。我只需要将调用作为std::ref()传递,如下所示:

std::向量线程;
void start()
{
const unsigned int numThreads=2;
for(int i=0;icontinueFlag.test_和_set(std::memory_order_released);
td->thread=std::thread(&work,i,std::ref(td->continueFlag));
线程。向后推(标准::移动(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));
    }
}
std::向量线程;
void start()
{
const unsigned int numThreads=2;
//创建新向量,调整大小不起作用,因为它试图分配/复制哪个原子_标志
//不支持
threads=std::vector(numThreads);
for(int i=0;i
我没有阅读代码,因此这可能不适用。但是
std::atomic_flag
的级别相当低,使用起来有点棘手。在我看来,
std::atomic
在这里更合适。它看起来就像一个普通的
bool
if(my_flag).
my_flag=true
my_标志=false。根据标准
std::atomic_标志
保证无锁。一个简单的工作线程测试增加一个由
while(continueFlag)
包装的计数器
std::atomic_标志
std::atomic
快100%以上。考虑到这是一个合成示例,它仍然表明如果您经常轮询标志,那么标志版本会更好。我倾向于在半关键部分进行投票,以了解工作是否被取消。除非您使用的是真正时髦的系统
std::atomic
,否则将不会被锁定。我非常怀疑这与
std::atomic_flag
之间在速度上的任何实际差异是否会明显。如果应用程序的性能受到检查原子变量的速度的限制,那么它没有做任何实际工作,需要重新设计。我不确定这是否正确,但是
线程。push_back(std::move(td))
应该调用
push_back
作为右值引用,因此应该调用默认的move构造函数。因此,如果“从对象移动”超出范围,这应该无关紧要。然而,我发现的问题是,
std::atomic_flag
既不可移动也不可复制(这毕竟是有道理的)。您对resize()的想法也不起作用,因为它还调用移动或复制赋值,因为它的内存被重新定位。但是一个向量的新副本是有效的。请看我的答案。@Sheph——这肯定很重要,因为在原始版本中,线程在被移动之前接收到对moved from对象的引用。不管你怎么移动它,线都不在乎。它获取对moved from对象的引用,随后移动它对引用没有影响,因此每个线程继续尝试访问被破坏的对象。未定义的行为。您的最终解决方案是正确的。
        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::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));
    }
}
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));
    }
}