C 仅递增/递减变量时是否需要互斥?

C 仅递增/递减变量时是否需要互斥?,c,synchronization,pthreads,mutex,C,Synchronization,Pthreads,Mutex,我有一个带整数字段的结构,比如 struct s { int a; int b; int c; int max; }; struct s mystruct = {0, 0, 0, 0}; // Global var 然后我有N个线程,它们有时必须在前三个字段上执行++或--之类的操作,有时必须读取它们,然后更新max字段。我必须在这里使用互斥吗?如果是,则仅在读取/更新max时需要,还是始终需要?为什么?如果我只是增加或减少前三个字段,那么一个线程是否在另一个线

我有一个带整数字段的结构,比如

struct s {
    int a;
    int b;
    int c;
    int max;
};
struct s mystruct = {0, 0, 0, 0}; // Global var

然后我有N个线程,它们有时必须在前三个字段上执行
++
--
之类的操作,有时必须读取它们,然后更新
max
字段。我必须在这里使用互斥吗?如果是,则仅在读取/更新
max
时需要,还是始终需要?为什么?如果我只是增加或减少前三个字段,那么一个线程是否在另一个线程之前运行有关系吗?

并发的通用规则是:如果以非原子方式并发访问内存位置,并且不是所有访问都是读取的,那么必须对访问进行排序。可以通过锁定互斥锁来序列化访问,也可以通过一些较低级别的操作(如有序原子访问或有序围栏)来实施排序


如果所有访问都是读取的,则唯一允许进行无序访问的时间。例如,多个线程可以读取(非原子)常量而无需排序。

如果正在执行的操作不是原子操作,则需要同步对变量的访问。 考虑单个变量<代码> A/COD>的初始值,例如,5。 现在我们有两个线程T1和T2,都想增加它。增量操作是如何分解的,如果它不是原子的(只是一个例子,可能是其他方式)

1) 将
值读入临时位置。
2) 递增并将临时位置写回
a

现在考虑一个场景T1先执行这个操作,然后在完成第1步之后先由T2抢占:

T1:1)将
a
读入
temp1
=>
temp1=5

T2:1)将
a
读入
temp2
=>
temp2=5

T2:2)将
temp2+1
写入
a
=>
a=6

T1:2)将
temp1+1
写入
a
=>
a=6


因此
a
的最终值将是
6
而不是
7
,这就像问你是否只需要锁上房子的前门一样,后门仅用于偶尔来访者,并不重要。请参阅,例如,您可以使用
stdatomic.h
有用的练习:定义“排序”的技术概念是“发生在之前”和“与同步”。访问不必排序。如果访问是原子性的,并且内存的顺序是松弛的,那么它们就没有顺序。