我谈到的例子是cppreference.com 上的这个例子。代码片段粘贴在下面。
int main(){
const std::size_t ThreadNumber = 5;
const int Sum = 5;
std::atomic<int> atom{0};
std::atomic<int> counter{0};
// lambda as thread proc
auto lambda = [&](const int id){
for (int next = 0; next < Sum;){
// each thread is writing a value from its own knowledge
const int current = atom.exchange(next);
counter++;
// sync writing to prevent from interrupting by other threads
std::osyncstream(std::cout)
<< '#' << id << " (" << std::this_thread::get_id()
<< ") wrote " …Run Code Online (Sandbox Code Playgroud) 我在几个地方读到,宽松的排序可以生成唯一的 ID。我对此表示怀疑,因为如果两个线程同时调用:
uniqueId.fetch_add(1, std::memory_order::relaxed);
那么线程 A 递增的值可能对线程 B 不可见。这意味着,两个线程可以获得相同的唯一 ID。
出于这个原因,我宁愿使用std::memory_order::acq_rel
你怎么认为?
在实践中无法测试。
使用现代 C++,拥有由一个线程初始化的共享内存(第一个到达这一点,然后由多个线程读取)的最佳方法是什么?它需要尽可能轻。
int *ptr = nullptr;
void parallel_work() {
// this should only done by the first thread to this point
ptr = init_ptr();
// all threads read After ptr is set
do_with(ptr);
}
int main() {
std::thread th0 { ¶llel_work };
std::thread th1 { ¶llel_work };
std::thread th2 { ¶llel_work };
parallel_work();
}
Run Code Online (Sandbox Code Playgroud)
如果它可以帮助的话,我真的想避免将代码的整个读取部分包装在mutex.
PS:这不是static函数变量的用例,因为我将在程序的生命周期中创建其中的许多变量。
阅读文本,因为std::condition_variable我遇到了这句话:
即使共享变量是原子的,也必须在互斥锁下进行修改,才能将修改正确发布到等待线程。
我的问题是这样的:
如果不是“与 POD 一起工作的无锁代码”,原子有什么用?
更新
看起来我的问题有些混乱:(
引用文本中的“共享变量”与“条件变量”不同。请参阅同一页面中的此引用:
...直到另一个线程同时修改共享变量(条件),并通知condition_variable
请不要回答“为什么我们需要使用带有条件变量的互斥锁”或“条件等待如何工作”,而是提供有关互斥锁的使用如何“正确发布”对等待线程的原子修改的信息,即是否需要在互斥锁下完成像++counter;(而不是像测试if(counter == 0))这样的表达式?
假设我有两个变量:
volatile int a = 0;
int b = 0;
Run Code Online (Sandbox Code Playgroud)
它们在两个线程之间共享。现在在第一个线程中,我按以下顺序修改这些变量:
a = 1;
b = 2;
Run Code Online (Sandbox Code Playgroud)
在第二个线程我做:
while (true) {
if (b == 2)
assert(a == 1);
}
Run Code Online (Sandbox Code Playgroud)
是否保证第二个线程永远不会失败?这意味着第二个线程读取出的写入的值a,并b以相同的顺序,他们是由第一个线程写的?
如您所见,我制作了avolatile 和bnon-volatile。所以我的问题是 volatile 修饰符是否对内存写入的顺序有任何保证?如果我b也制作volatile它会改善情况吗?
或者,只有这样,才能保证顺序是使用std::atomic<int>两个a及b?
怎么样std::mutex?如果我通过两个线程上的单个共享互斥锁保护两个变量并使用非易失性变量,它是否有助于内存排序?也就是说,如果我做下一个(包括a和b是非易失性的):
int a = 0, b = 0; // shared
std::mutex m; // shared
// .... In Thread 1 ....
{ …Run Code Online (Sandbox Code Playgroud) 我知道GCC 链接库-latomic需要该标志。atomic但是,由于某种原因std::atomic<int>不需要它来构建
而结构体则
这种差异是由什么造成的呢?
我注意到,LONG InterlockedIncrement(LONG* p)不仅返回增量,还增加了p它自身地址的值.这意味着,作为调用者,您既可以使用返回值,也可以使用指向的值p.
这很好,事实上,我可以很好地使用它,但文档中没有提到它.
可以使用修改后的值吗?这种行为是否可以在未来版本的Windows API中更改
在 Go 中,使用sync.Mutexor来防止共享对象的并发访问。chan但是,在某些情况下,我只对对象的变量或字段的“最新”值感兴趣。或者我喜欢写一个值,并不关心另一个 go 例程稍后会覆盖它还是之前刚刚覆盖它。
更新: TLDR;只是不要这样做。这是不安全的。阅读答案、评论和链接文档!
2021 年更新: Go 内存模型将被更彻底地指定, Russ Cox 撰写的三篇精彩文章将教您更多关于不同步内存访问的令人惊讶的影响。这些文章总结了以下许多讨论和学习内容。
以下是示例程序的两个变体,两者似乎都使用当前的 Go 运行时产生“正确”的good输出:bad
package main
import (
"flag"
"fmt"
"math/rand"
"time"
)
var bogus = flag.Bool("bogus", false, "use bogus code")
func pause() {
time.Sleep(time.Duration(rand.Uint32()%100) * time.Millisecond)
}
func bad() {
stop := time.After(100 * time.Millisecond)
var name string
// start some producers doing concurrent writes (DANGER!)
for i := 0; i < 10; i++ { …Run Code Online (Sandbox Code Playgroud) 考虑以下posix线程代码:
void *thr(void *arg)
{
static int i = 0;
return ++i;
}
Run Code Online (Sandbox Code Playgroud)
我认为这是原子安全的操作,因为它应该转换为汇编指令inc i以改变价值.但是,我不是cpu和多线程内部的伟大专家,因此我很怀疑.
编辑:因为它不安全所以说我使用互斥锁来使这个安全,然后在时间方面需要多少额外费用.编辑:我从pthread互斥量的开销得到我的回答时间成本.谢谢一堆.