相关疑难解决方法(0)

产生一个线程是否自己提供内存顺序保证?

我想大致这样做:

初始线程:

  • 将一些值写入全局变量(它们将永远不会被再次写入)
    • 这可能是中等大的数据(数组、字符串等)。不能简单地制造std::atomic<>
  • 产生其他线程

其他线程:

  • 读取全局状态
  • 做工作等

现在,我知道我可以将参数传递给std::thread,但我试图通过这个例子来理解 C++ 的内存保证。

此外,我非常有信心,在任何现实世界的实现中,创建线程都会导致内存障碍,确保线程可以“看到”父线程在此之前编写的所有内容。

但我的问题是:这是由标准保证的吗?

旁白:我想我可以添加一些虚拟的东西std::atomic<int>,并在启动其他线程之前写入,然后在其他线程上,在启动时读取一次。我相信所有的happens-before 机制都可以保证先前编写的全局状态是正确可见的。

但我的问题是,技术上是否需要这样的东西,或者线程创建是否足够?

c++ multithreading

23
推荐指数
1
解决办法
1264
查看次数

使用4个线程获取/释放语义

我目前正在阅读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)

Thread a (x is now true)
Thread c (fails to increment z)
Thread b (y …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading memory-model memory-barriers stdatomic

19
推荐指数
2
解决办法
851
查看次数

在屏障实现中将代码从顺序一致性更改为不太严格的排序

我在《C++ Concurrency in Action》一书中遇到了这段代码,用于简单实现屏障(对于不能std::experimental::barrier在 C++17 或std::barrierC++20 中使用的代码)。

[编辑]屏障是一种同步机制,其中一组线程(线程数传递给屏障的构造函数)可以到达并等待(通过调用 wait 方法)或到达并丢弃(通过调用 did_waiting) 。如果组中的所有线程都到达屏障,则屏障将被重置,并且线程可以继续执行下一组操作。如果组中的某些线程脱落,则组中的线程数量相应减少,以进行下一轮与屏障的同步。[编辑结束]

以下是为简单实现屏障而提供的代码。

struct barrier
{
   std::atomic<unsigned> count;
   std::atomic<unsigned> spaces;
   std::atomic<unsigned> generation;
   barrier(unsigned count_):count(count_),spaces(count_),generation(0)
   {}
   void wait(){
      unsigned const gen=generation.load();
      if(!--spaces){
         spaces=count.load();
         ++generation;
      }else{
         while(generation.load()==gen){
            std::this_thread::yield();
         }
      }
   }
   void done_waiting(){
      --count;
      if(!--spaces){
         spaces=count.load();
         ++generation;
      }
   }
};
Run Code Online (Sandbox Code Playgroud)

作者 Anthony Williams 提到,他选择顺序一致性排序是为了更容易推理代码,并表示可以使用宽松的排序来提高代码效率。这就是我更改代码以采用宽松排序的方法。请帮助我理解我的代码是否正确。

struct barrier
{
   std::atomic<unsigned> count;
   std::atomic<unsigned> spaces;
   std::atomic<unsigned> generation;
   barrier(unsigned count_):count(count_),spaces(count_),generation(0)
   {}
   void wait(){
      unsigned const gen=generation.load(std::memory_order_acquire);
      if(1 == spaces.fetch_sub(1, std::memory_order_relaxed)){
         spaces=count.load(std::memory_order_relaxed);
         generation.fetch_add(1, std::memory_order_release); …
Run Code Online (Sandbox Code Playgroud)

c++ atomic memory-barriers barrier stdatomic

7
推荐指数
1
解决办法
194
查看次数

在没有 seq-cst 负载的情况下,seq-cst 栅栏与 acq-rel 栅栏完全相同吗?

我试图了解std::atomic_thread_fence(std::memory_order_seq_cst);栅栏的用途,以及它们与栅栏有何不同acq_rel

到目前为止,我的理解是,唯一的区别是 seq-cst 栅栏影响 seq-cst 操作的全局顺序 ( [atomics.order]/4)。并且只有在实际执行 seq-cst 加载时才能观察到所述顺序。

所以我想,如果我没有 seq-cst 负载,那么我可以用 acq-rel 栅栏替换所有 seq-cst 栅栏,而不改变行为。那是对的吗?

如果这是正确的,为什么我会看到这样的代码“使用栅栏实现 Dekker 算法”,它使用 seq-cst 栅栏,同时保持所有原子读/写宽松?这是该博客文章中的代码:

std::atomic<bool> flag0(false),flag1(false);
std::atomic<int> turn(0);

void p0()
{
    flag0.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag1.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 0)
        {
            flag0.store(false,std::memory_order_relaxed);
            while (turn.load(std::memory_order_relaxed) != 0)
            {
            }
            flag0.store(true,std::memory_order_relaxed);
            std::atomic_thread_fence(std::memory_order_seq_cst);
        }
    }
    std::atomic_thread_fence(std::memory_order_acquire);
 
    // critical section


    turn.store(1,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_release);
    flag0.store(false,std::memory_order_relaxed);
}

void p1()
{
    flag1.store(true,std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_seq_cst);

    while (flag0.load(std::memory_order_relaxed))
    {
        if (turn.load(std::memory_order_relaxed) != 1)
        {
            flag1.store(false,std::memory_order_relaxed);
            while …
Run Code Online (Sandbox Code Playgroud)

c++ concurrency atomic language-lawyer stdatomic

6
推荐指数
1
解决办法
335
查看次数

C++ Concurrency in Action 中危险指针的实现是否存在缺陷?

我正在阅读《C++ Concurrency in Action》第二版。下面的代码来自清单 7.6。它pop()使用危险指针来实现堆栈。

std::shared_ptr<T> pop() {
  std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
  node* old_head = head.load();  // #1
  do {
    node* temp;
    do {
      temp = old_head;
      hp.store(old_head);        // #2
      old_head = head.load();    // #3
    } while (old_head != temp);  // #4
  } while (old_head &&
           !head.compare_exchange_strong(old_head, old_head->next));
  hp.store(nullptr);
  // ...
}
Run Code Online (Sandbox Code Playgroud)

书中解释了内循环的作用:

您必须在while循环中执行此操作,以确保node在读取旧head指针#1和设置危险指针#2之间没有删除。在此窗口期间,没有其他线程知道您正在访问该特定节点。幸运的是,如果旧head节点要被删除,head那么它本身一定已经发生了变化,因此您可以检查这一点并继续循环,直到您知道该head指针仍然具有与您设置危险指针相同的值#4

根据 的实现,如果另一个线程在和之间pop删除了头节点,则将被修改为新节点。pop …

c++ concurrency multithreading atomic stdatomic

5
推荐指数
1
解决办法
434
查看次数

是否会在其他线程中始终以相同顺序看到对不同线程中不同位置的两次原子写操作?

与我之前的问题类似,请考虑以下代码

-- 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)在他著名的- …

c++ concurrency memory-model c++11 stdatomic

4
推荐指数
3
解决办法
540
查看次数