当将指针传递到C++11中的另一个线程时,会出现竞争条件

When is passing a pointer to another thread in C++11 a race condition?

本文关键字:线程 条件 竞争 另一个 指针 C++11      更新时间:2023-10-16

此代码是否具有竞争条件?

auto x = make_shared<Fun>(1984); //could also be raw ptr
auto t = thread(func, x);

Herb Sutter在C++和Beyond的演讲中也使用了并发队列。

所以如果我有一个队列q

auto x  = new Fun(1984);
q.push(x);
//other thread 
auto ptr = q.pop();

这是比赛条件吗?另一个线程是否可以弹出ptr,并且仍然可以看到ptr所指向的内存位置的陈旧值?由于并发队列不在标准中,所以假设我使用的是PPL/TBB队列。

在这两种情况下均为否。

如果不是因为std::thread的一些特殊性质,第一个例子在原则上是一个确定的竞赛条件。原则上允许编译器对移动进行重新排序,但是构造线程对象具有顺序一致性,这在形式上保证了它的工作。但是,即使它没有这个属性,在实践中创建和启动一个线程也需要相当长的时间(几十万到数百万倍于执行几个移动指令的时间),所以你实际上(不是正式的,而是实际的)可以保证没有种族,即使没有顺序一致性(这种"实际保证"确实有点争议,在悔过的极端情况下可能不成立,但形式保证无论如何都是正确的)。

第二个示例使用并发队列,根据定义,该队列是以不发生竞争条件的方式进行设计的(使用锁或使用无锁算法)1

在任何一种情况下,最终在队列中的指针都保证根本不在那里(在这种情况下,pop操作将阻塞或失败,其中"失败"意味着使用线程知道此时没有可用的有效指针——它不会假设某个无效值),或者它处于有效状态。将指针添加到队列的原子(锁定或无锁)操作保证了之前发生的对象构造也在之前实现(即队列上没有指针而没有有效对象)
这意味着当从队列中检索对象的指针时,对象也必须是有效的(除非您作弊)。


1这显然假设了队列的正确操作,但这是一个合理的假设。

可能存在竞争条件,但前提是当其他线程开始使用其副本时使用指针x

像这样的函数:

void CreateWork(Queue& q){
    auto x  = new Fun(1984);
    // could use x here
    q.push(x);
}

因为一旦x在队列中,调用CreateWork的线程就不能访问x