我目前正在阅读Anthony Williams的C++ Concurrency in Action.他的一个列表显示了这段代码,他声明z != 0
可以解雇的断言.
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
x.store(true,std::memory_order_release);
}
void write_y()
{
y.store(true,std::memory_order_release);
}
void read_x_then_y()
{
while(!x.load(std::memory_order_acquire));
if(y.load(std::memory_order_acquire))
++z;
}
void read_y_then_x()
{
while(!y.load(std::memory_order_acquire));
if(x.load(std::memory_order_acquire))
++z;
}
int main()
{
x=false;
y=false;
z=0;
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join();
b.join();
c.join();
d.join();
assert(z.load()!=0);
}
Run Code Online (Sandbox Code Playgroud)
所以我能想到的不同执行路径是这样的:
1)
Run Code Online (Sandbox Code Playgroud)Thread a (x is now true) Thread c (fails to increment z) Thread b (y …
即使对于一个简单的双线程通信示例,我也难以用C11原子和memory_fence样式来表达它以获得正确的内存排序:
共享数据:
volatile int flag, bucket;
Run Code Online (Sandbox Code Playgroud)
生产者线程:
while (true) {
int value = producer_work();
while (atomic_load_explicit(&flag, memory_order_acquire))
; // busy wait
bucket = value;
atomic_store_explicit(&flag, 1, memory_order_release);
}
Run Code Online (Sandbox Code Playgroud)
消费者线程:
while (true) {
while (!atomic_load_explicit(&flag, memory_order_acquire))
; // busy wait
int data = bucket;
atomic_thread_fence(/* memory_order ??? */);
atomic_store_explicit(&flag, 0, memory_order_release);
consumer_work(data);
}
Run Code Online (Sandbox Code Playgroud)
据我所知,上面的代码可以正确地命令存储在桶中 - > flag-store - > flag-load - > load-from-bucket.但是,我认为在load-from-bucket和使用新数据重新写入桶之间仍存在竞争条件.要在读取桶之后强制执行订单,我想我需要atomic_thread_fence()
在读取桶和以下atomic_store之间进行显式操作.不幸的是,似乎没有任何memory_order
论据可以对前面的负载强制执行任何操作,甚至没有memory_order_seq_cst
.
一个非常脏的解决方案可能是bucket
在消费者线程中使用虚拟值重新分配:这与消费者只读概念相矛盾.
在较旧的C99/GCC世界中,我可以使用__sync_synchronize()
我认为足够强大的传统.
什么是更好的C11风格的解决方案来同步这种所谓的反依赖?
(当然我知道我应该更好地避免这种低级编码并使用可用的更高级别的结构,但我想了解...)
TL:DR:如果互斥体实现使用获取和释放操作,那么实现是否可以像通常允许的那样进行编译时重新排序,并重叠两个应该独立于不同锁的临界区?这将导致潜在的僵局。
假设互斥锁在 上实现std::atomic_flag
:
struct mutex
{
void lock()
{
while (lock.test_and_set(std::memory_order_acquire))
{
yield_execution();
}
}
void unlock()
{
lock.clear(std::memory_order_release);
}
std::atomic_flag lock; // = ATOMIC_FLAG_INIT in pre-C++20
};
Run Code Online (Sandbox Code Playgroud)
到目前为止看起来还可以,关于使用单个这样的互斥锁:std::memory_order_release
与std::memory_order_acquire
.
在这里使用std::memory_order_acquire
/std::memory_order_release
不应该一见钟情。它们类似于 cppreference 示例https://en.cppreference.com/w/cpp/atomic/atomic_flag
现在有两个互斥锁保护不同的变量,两个线程以不同的顺序访问它们:
mutex m1;
data v1;
mutex m2;
data v2;
void threadA()
{
m1.lock();
v1.use();
m1.unlock();
m2.lock();
v2.use();
m2.unlock();
}
void threadB()
{
m2.lock();
v2.use();
m2.unlock();
m1.lock();
v1.use();
m1.unlock();
}
Run Code Online (Sandbox Code Playgroud)
释放操作可以在无关的获取操作之后重新排序(无关操作 == 对不同对象的后续操作),因此执行可以转换如下:
mutex m1;
data v1;
mutex …
Run Code Online (Sandbox Code Playgroud) 与我之前的问题类似,请考虑以下代码
-- Initially --
std::atomic<int> x{0};
std::atomic<int> y{0};
-- Thread 1 --
x.store(1, std::memory_order_release);
-- Thread 2 --
y.store(2, std::memory_order_release);
-- Thread 3 --
int r1 = x.load(std::memory_order_acquire); // x first
int r2 = y.load(std::memory_order_acquire);
-- Thread 4 --
int r3 = y.load(std::memory_order_acquire); // y first
int r4 = x.load(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
是怪异的结果 r1==1, r2==0
,并r3==2, r4==0
有可能在C ++ 11内存模型下,这种情况下?如果我要全部替换std::memory_order_acq_rel
成该std::memory_order_relaxed
怎么办?
在x86上,这样的结果似乎是被禁止的,请参见此SO问题,但我一般是在询问C ++ 11内存模型。
奖励问题:
我们都同意,与std::memory_order_seq_cst
该怪异的结果不会在C ++ 11被允许。现在,赫伯·萨特(Herb Sutter)在他著名的- …
这是 Java 库中的一个片段:
public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) {
return (int)VALUE.compareAndExchangeAcquire(this,
(expectedValue ? 1 : 0),
(newValue ? 1 : 0)) != 0;
}
Run Code Online (Sandbox Code Playgroud)
它来自AtomicBoolean
课堂。演员怎么能int
返回一个boolean
?
我的主要问题:compareAndExchange
vs之间有什么区别compareAndExchangeAcquire
?
通俗地说:在申请之前xxxAcquire
和之后写的语句xxxRelease
可以自由重新排序xxx
。