标签: stdatomic

std::atomic_ref 需要对齐

根据cppreference,硬件可能要求 an 引用的对象atomic_ref<T>比其他T对象具有更严格的对齐方式,并且 an 上的操作是否atomic_ref是无锁的可以取决于引用对象的对齐方式。

为什么只需要引用的对象具有atomic_ref适当的对齐方式,而std::atomic似乎没有强加这一要求?

c++ atomic memory-alignment stdatomic c++20

4
推荐指数
1
解决办法
288
查看次数

动态调度的正确 std::atomic 内存顺序是什么?

我想知道经典的“原子计数器动态调度”惯用法的正确内存顺序是什么。那是:

  1. i使用 fetch-add 获取下一个要处理的元素的索引
  2. 如果i超出数组末尾,则终止
  3. i线程安全地处理元素,因为没有其他线程可以拥有i
  4. 转到1。

例如:

#include <atomic>

std::atomic_int counter = 0;

void foo(int *data, int size) {
    // we could also write counter++
    for (int i; (i = counter.fetch_add(1, std::memory_order::seq_cst)) < size;) {
        data[i] *= 2;
    }
}
Run Code Online (Sandbox Code Playgroud)
// driver code
#include <thread>
#include <numeric>
#include <cassert>

int main() {
    int data[1'000'000];
    std::iota(std::begin(data), std::end(data), 0);

    std::thread other{foo, data, std::size(data)};
    foo(data, std::size(data));
    other.join();

    for (int i = 0; i < std::size(data); ++i) …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading atomic stdatomic

4
推荐指数
1
解决办法
222
查看次数

C++ 20中acquire操作的定义在哪里?

我正在阅读cppreference.com 上的std::memory_order和C++ 20 规范。我找不到术语“获取操作”和“释放操作”的定义。

第一个文档递归地定义了这些术语,表示“具有 memory_order_acquire 或更强的原子加载是一个获取操作”。和“memory_order_acquire:使用此内存顺序的加载操作在受影响的内存位置上执行获取操作。”

第二份文件似乎使用了这些术语,但没有任何定义。它开始使用这些术语,表示“一个或多个内存位置上的同步操作要么是消耗操作、获取操作、释放操作,要么是获取和释放操作。”

所以我用谷歌搜索并找到了LINUX KERNEL MEMORY BARRIERS,它清楚地定义了如下术语。

(5) 获取操作。

这起到了单向渗透屏障的作用。它保证 ACQUIRE 操作之后的所有内存操作看起来都发生在相对于系统的其他组件的 ACQUIRE 操作之后。ACQUIRE 操作包括 LOCK 操作以及 smp_load_acquire() 和 smp_cond_load_acquire() 操作。

在 ACQUIRE 操作之前发生的内存操作可能看起来是在 ACQUIRE 操作完成之后发生的。

ACQUIRE 操作几乎总是与 RELEASE 操作配对。

这个定义与作者对术语“获取操作”的含义完全相同吗?

c++ atomic memory-barriers language-lawyer stdatomic

4
推荐指数
1
解决办法
197
查看次数

std :: atomic变量应该使用"normal"语法还是"load"和"store"?

如果我有一个原子变量,例如,

std::atomic<int> x;
Run Code Online (Sandbox Code Playgroud)

我想对它执行读写操作,我可以使用"普通"语法,例如,

std::cout << x;       // read from x
x = 5;                // write to x
Run Code Online (Sandbox Code Playgroud)

我也可以使用显式loadstore成员函数:

std::cout << x.load();       // read from x
x.store(5);                  // write to x
Run Code Online (Sandbox Code Playgroud)

我见过Andrei Alexandrescu和Anthony Williams等人的建议只使用了表达loadstore形式,大概是因为"正常"形式并没有强调变量是原子的.这似乎就像一种匈牙利符号.是否有关于在读写原子时使用的语法的新兴惯例?

c++ atomic c++11 stdatomic

3
推荐指数
1
解决办法
560
查看次数

std :: atomic是否提供原子行为,无论是否排序?

如果使用std::atomic模板声明变量,例如std::atomic<int>,是否保证通过方法访问std::atomic将导致一致的值(即通过std::atomic方法写入的值),而不管顺序如何?

据我所知,这相当于询问读取写入是否可以"撕裂" - 即在ISA级别可见的多个部分中写入或写入.

c++ concurrency memory-model stdatomic

3
推荐指数
1
解决办法
123
查看次数

std :: atomic错误:没有为后缀'++'声明'operator ++(int)'[-fpermissive]

我试图atomic通过不同的线程更新变量,并得到此错误。这是我的代码。

class counter {
    public:
    std::atomic<int> done;

    bool fn_write (int size) const {
        static int count = 0;
        if (count == size) {
            done++;
            count = 0;
            return false;
        } else {
            count++;
            return true;
        }
    }
};

int main() {
    counter c1;
    for (int i=0; i<50; i++) {
        while (! c1.fn_write(10)) ;
    }
}
Run Code Online (Sandbox Code Playgroud)

我在第8行中遇到以下错误done++

错误:没有为后缀'++'声明'operator ++(int)'[-fpermissive]

c++ const c++11 stdatomic

3
推荐指数
1
解决办法
388
查看次数

是否可以使用std :: atomic与POD结构,除了它有一个construtor?

我正在使用一些原子变量,所有unsigned int,我想将它们收集到一个结构中 - 实际上是一个POD.但是我也想要一个构造函数,因为我的编译器不是c ++ 11(所以我必须定义自己的构造函数来创建初始值).

所以最初我有:

// Names are not the real names - this is just for example
std::atomic<int> counter1;
std::atomic<int> counter2;
std::atomic<int> counter3;
Run Code Online (Sandbox Code Playgroud)

然后我很乐意根据需要增加/减少它们.但后来我决定再多一些计数器,因此将它们放入一个结构中:

struct my_counters {
    int counter1;
    int counter2;
    int counter3;
    // Constructor so that I can init the values I want.
    my_counters(c1, c2, c3) : counter1(c1), counter2(c2), counter3(c3){;}
};
Run Code Online (Sandbox Code Playgroud)

但是因为我添加了一个自定义构造函数,所以这在技术上不再是POD.我正在阅读有关此问题的其他问题,他们说使用std :: atomic我需要一个POD,但我读到的其他问题表明结构需要是可复制的或者一些这样的...无论如何,我感到很困惑,我想要要知道我是否可以安全地使用我的结构my_counters作为原子类型:

std::atomic<my_counters> counters;
Run Code Online (Sandbox Code Playgroud)

然后在各种线程内:

// Are these operations now still atomic (and therefore safe to use across threads):
counters.counter1++;
counters.counter2--;
counters.counter3 …
Run Code Online (Sandbox Code Playgroud)

c++ struct stdatomic

3
推荐指数
2
解决办法
1030
查看次数

是否是线程安全的,当它在主线程中填充数据时,从结构数组的较低索引元素读取

原始问题:

我得到了一个结构数组,并在主线程中读取它时将其填充到一个单独的线程中:

struct DataModel MyData[1024];

struct DataModel
{
    bool IsFilled;
    float a;
    float b;
}
Run Code Online (Sandbox Code Playgroud)
  • 我有一个Thread,它将Mydata数组从0索引填充到最后一个索引(在上面是1024).

  • 然后我从填充线程中获取最后一个填充的结构索引.

  • 然后我尝试读取元素的值,其中一个索引低于填充的索引.

  • 我们假设当第500个元素被填充时,我从MyData数组的499元素中读取值,所以我确保我没有读取正在写入的数组元素.

Q1:这个线程安全吗?

Q2:是否有可能发生未定义的行为或误读vales?


进一步编辑:

问题是编辑不当以添加更多细节,这就是为什么它引入了不一致的答案,所以我分开了以前的编辑,以提高答案和接受答案的一致性.

编辑1:这是可能实施的建议.虽然它可能显示错误的结果,但只是我想询问线程安全和未定义的行为,以下解决方案可能会显示各种结果,但我试图首先询问线程安全性.

std::atomic<int> FilledIndex;
    void FillingMyData(struct DataModel myData[])
    {
      for(size_t i = 0; i < 1024; i++)
      {
        myData[i].a = rand();
        myData[i].b = rand();
        myData[i].IsFilled = true;

    FilledIndex = i;
  }
}

int main()
{
     std::thread ReadThread(FillingMyData, MyData);
     while(FilledIndex < 1024)
     {
          std::cout << MyData[FilledIndex].a;
     }
     ReadThread.join();
     return 0;
}
Run Code Online (Sandbox Code Playgroud)

c++ multithreading producer-consumer stdatomic

3
推荐指数
1
解决办法
146
查看次数

是什么从形式上保证了非原子变量不会看到空气中的稀疏值,并且在理论上可以像原子弛豫一样创建数据竞争呢?

这是有关C ++标准的形式保证的问题。

该标准指出,std::memory_order_relaxed原子变量规则允许“凭空” /“出乎意料”的值出现。

但是对于非原子变量,这个例子可以有UB吗?是否r1 == r2 == 42有可能在C ++抽象机?== 42最初都不是变量,因此您不希望任何if主体执行,这意味着不会写入共享变量。

// Global state
int x = 0, y = 0;

// Thread 1:
r1 = x;
if (r1 == 42) y = r1;

// Thread 2:
r2 = y;
if (r2 == 42) x = 42;
Run Code Online (Sandbox Code Playgroud)

上面的示例改编自标准,该标准明确表示原子对象规范允许这种行为

[注意:在以下示例中,要求确实允许r1 == r2 == 42,而x和y最初为零:

// Thread 1:
r1 = x.load(memory_order_relaxed);
if (r1 == 42) y.store(r1, memory_order_relaxed);
// …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading language-lawyer stdatomic data-race

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

单个编写者+读者的发布/检查更新类可以使用memory_order_relaxed还是获取/发布以提高效率?

介绍

我有一个小类,它使用std :: atomic进行无锁操作。由于该课程被广泛调用,因此影响了性能,并且遇到了麻烦。

类说明

该类类似于LIFO,但是一旦调用pop()函数,它仅返回其环形缓冲区的最后写入元素(仅当自上次pop()之后存在新元素时)。

一个线程正在调用push(),另一个线程正在调用pop()。

我读过的资料

由于这占用了我的计算机时间太多,因此我决定进一步研究std :: atomic类及其memory_order。我已经阅读了很多StackOverflow以及其他来源和书籍中的memory_order帖子,但是我无法对不同的模式有一个清晰的了解。特别是,我在获取和释放模式之间挣扎:我也看不出为什么它们与memory_order_seq_cst不同。

根据我自己的研究,我认为每个记忆顺序都是用我的话做的

memory_order_relaxed:在同一线程中,原子操作是即时的,但是其他线程可能无法立即看到最新的值,它们将需要一些时间才能被更新。编译器或OS可以自由地对代码进行重新排序。

memory_order_acquire / release:由atomic :: load使用。它防止重新排序之前存在的代码行(编译器/ OS可能在此行之后对其重新排序),并使用此线程或另一个线程中的memory_order_releasememory_order_seq_cst读取存储在此原子上的最新值。memory_order_release还可以防止对该代码重新排序之后的代码。因此,在获取/发布中,两者之间的所有代码都可以被OS改组。我不确定这是在同一线程还是不同线程之间。

memory_order_seq_cst:最容易使用,因为就像我们使用变量的自然写法一样,立即刷新其他线程加载函数的值。

LockFreeEx类

template<typename T>
class LockFreeEx
{
public:
    void push(const T& element)
    {
        const int wPos = m_position.load(std::memory_order_seq_cst);
        const int nextPos = getNextPos(wPos);
        m_buffer[nextPos] = element;
        m_position.store(nextPos, std::memory_order_seq_cst);
    }

    const bool pop(T& returnedElement)
    {

        const int wPos = m_position.exchange(-1, std::memory_order_seq_cst);
        if (wPos != -1)
        {
            returnedElement = m_buffer[wPos]; …
Run Code Online (Sandbox Code Playgroud)

c++ atomic lock-free memory-barriers stdatomic

3
推荐指数
1
解决办法
128
查看次数