我首先引用一些描述来自" C ++并发行动"安东尼威廉姆斯:
class spinlock_mutex
{
std::atomic_flag flag;
public:
spinlock_mutex():
flag(ATOMIC_FLAG_INIT)
{}
void lock()
{
while(flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
lock()操作是对flag.test_and_set()的循环使用 std :: memory_ order_acquire命令,而unlock()是一个调用 flag.clear()与std :: memory_order_release排序。当第一个 线程调用lock(),标志最初是清除的,所以第一次调用 test_and_set()将设置标志并返回false,表示 这个线程现在有锁,并终止循环。线程是 然后可以自由修改受互斥锁保护的任何数据。任何其他线程 在这个时候调用lock()会发现已设置的标志并且将会 在test_and_set()循环中被阻止。
带锁的线程完成后修改受保护的 数据,它调用unlock(),它调用flag.clear() std :: memory_order_release语义。然后与之同步(参见 第5.3.1节)从一个后续调用flag.test_and_set() 在另一个线程上调用lock(),因为这个调用有 std :: memory_order_acquire语义。因为修改了 受保护的数据必须在unlock()调用之前排序 修改发生在unlock()之前,因此发生在之前 从第二个线程调用后续的lock()(因为 与unlock()和lock()之间的关系同步 并且在从第二个线程访问该数据之前发生 一旦获得锁定。
问:如果只有两个线程,并且线程A第一次让对象m1
调用lock()
,而线程B使对象m1
调用lock()
} m1
在线程A中调用unlock()
之前的第一次,flag.test_and_set(std::memory_order_acquire)
在m1
调用lock
时为什么std::memory_order_release
变为true而不是false(初始值)线程B中的函数?
我知道发布序列,但构成发布序列需要一个原子对象调用std::memory_order_release
的原子操作,并且没有Get get = Http.get(url + "/eds/api/v1/certificados");
get.header("Authorization", "Basic " + Credentials);
get.header("APIKey", APIKey);
get.header("Accept", "application/json");
System.out.println(get.text());
调用的操作。
答案 0 :(得分:1)
acquire
和release
语义与其他(受保护)资源相关,此处未显示。特别是,在锁定之后或解锁之前不要移动访问权限。原子操作本身是完全有序的。
由于操作是完全有序的,因此两个线程都会以相同的顺序看到您的假设订单A:lock, B:lock, A:unlock
。因此,当线程B调用lock
时,它只能看到来自A的lock
而不是unlock
。
答案 1 :(得分:1)
线程彼此先行,除了具有您希望了解的行为外,它实际上没有任何意义。 memory_order不在其中。它指定了如何在原子操作周围对常规的非原子内存访问进行排序。
拥有它的原因是:
lock();
foo();
unlock();
在两个线程中,一个线程中的foo无法在相关线程的锁定或解锁之前或之后读取或读取。这与锁定和解锁本身的原子性相结合,给出了我们期望的行为。 (即,没有来自foo的并发访问)。
答案 2 :(得分:0)
只有一个std::atomic_flag
。在任何时候,都可以设置(true
)或清除(false
)。
std::atomic_flag::test_and_set
定义为
以原子方式将
std::atomic_flag
的状态更改为设置(true
)并返回之前保存的值。
当A调用lock
时,它已将标志更改为已设置,因此设置了B尝试锁定时返回的状态。这被评估为while
的条件,因此循环继续。线程B将在此循环中继续“旋转”直到锁定被释放
最后,当A调用解锁时,标志会变为清除。然后B可以再次测试,false
结束循环。