Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 确保整体读取结构_C_Multithreading_Gcc_Consistency - Fatal编程技术网

C 确保整体读取结构

C 确保整体读取结构,c,multithreading,gcc,consistency,C,Multithreading,Gcc,Consistency,我在两个线程之间有一个共享结构变量: struct { long a; long b; long c; } myStruct; struct myStruct A; A的所有3个字段都初始化为零。然后第一个线程将更新它们: A.a = 1; A.b = 2; A.c = 3; 第二个线程将从中读取。我想确保的是,第二个线程将读取一个整体,要么是旧值{0,0,0},要么是新值{1,2,3},而不是像{1,2,0}这样的损坏。 该结构不适合64位,因此我无法使用gcc的内置原子,而且

我在两个线程之间有一个共享结构变量:

struct {
  long a;
  long b;
  long c;
} myStruct;
struct myStruct A;
A的所有3个字段都初始化为零。然后第一个线程将更新它们:

A.a = 1;
A.b = 2;
A.c = 3;
第二个线程将从中读取。我想确保的是,第二个线程将读取一个整体,要么是旧值{0,0,0},要么是新值{1,2,3},而不是像{1,2,0}这样的损坏。 该结构不适合64位,因此我无法使用gcc的内置原子,而且我也不想使用互斥,因此我提出了两个保护标志:

struct {
  long a;
  long b;
  long c;
  volatile int beginCount, endCount;
} A;
然后第一个线程将:

A.beginCount++;
A.a = 1;
A.b = 2;
A.c = 3;
A.endCount++;
第二个将循环,直到得到一致的结构:

int begin, end;
myStruct tmp;
do {
  begin = A.beginCount;
  end = A.endCount;
  tmp = A;
} while (!(begin == A.beginCount && end == A.endCount && A.beginCount == A.endCount))
// now tmp will be either {0,0,0} or {1,2,3}
那两面护旗够了吗?如果没有,那么请指出可能会破坏它的线程调度的具体组合

编辑1:我不想使用互斥的原因是第一个线程具有高优先级,它不应该等待任何东西。如果第一个线程想在第二个线程读取时写入,那么第一个线程仍然会写入,第二个线程必须重新读取,直到获得一致的值。我们不能用互斥,至少我不知道

编辑2:关于环境:这段代码运行在多处理器系统上,我为每个线程指定了一个完整的cpu核心

编辑3:我知道没有互斥或原子的同步非常棘手。我已经列出了我能想到的所有组合,但找不到任何一个破坏代码的组合。所以,请不要只是告诉我它不起作用,如果你能指出它什么时候会坏,我将非常感激

我也不想使用互斥

在单处理器系统上,如果第一个线程在写入时被抢占,则读取线程将花费不必要的时间片旋转。在这种情况下,您确实需要一个互斥体

Linux和Windows在非争用情况下以及在多处理器系统上都不进行上下文切换,在让步之前先旋转一段时间

为什么要重新实施完全相同的机制

我也不想使用互斥

在单处理器系统上,如果第一个线程在写入时被抢占,则读取线程将花费不必要的时间片旋转。在这种情况下,您确实需要一个互斥体

Linux和Windows在非争用情况下以及在多处理器系统上都不进行上下文切换,在让步之前先旋转一段时间


为什么要重新实现完全相同的机制?

绝对没有可移植的方式来实现您想要的功能。一些非常高端的系统具有事务性内存,可以实现您想要的功能,但无论如何,使用事务性内存的正常模式是使用锁编写代码,并依赖锁实现来使用事务


只需使用互斥来保护读写。没有其他方法可以使您的代码正确,但是有很多方法可以使代码“在测试中看起来正确”,直到它违反不变量并在几个月后崩溃,或者在稍微不同的环境/cpu上运行,并且每次运行时都开始崩溃。

绝对没有可移植的方法来做您想要的事情。一些非常高端的系统具有事务性内存,可以实现您想要的功能,但无论如何,使用事务性内存的正常模式是使用锁编写代码,并依赖锁实现来使用事务


只需使用互斥来保护读写。没有其他方法可以使代码正确,但是有很多方法可以使代码“在测试中看起来是正确的”,直到它违反不变量并在几个月后崩溃,或者在稍微不同的环境/cpu上运行,并且每次运行时都开始崩溃。

我的第一个建议是,您确实应该使用互斥锁来实现它(确保每个线程持有互斥锁的时间尽可能短)并查看是否确实遇到任何问题。最有可能的情况是,您会发现使用互斥锁可以很好地工作,不需要更多。这样做的优点是可移植到任何硬件,易于理解,易于调试

也就是说,如果你坚持不使用互斥,那么唯一的其他选择就是使用原子变量。由于原子变量是字大小的,你将无法使整个结构成为原子结构,但你可以通过实例化一个结构数组来伪造它(数组的必要大小将取决于您打算更新结构的频率)然后使用原子整数作为数组中“当前可读取”和“可写入可写入”结构的索引。从数组中读取结构的当前值非常简单——只需从数组中的“current valid for reading”索引保证不会写入,但写入一个新值更为复杂;您需要原子级地增加“okay to write to write”索引(如有必要,请将其环绕以避免索引脱离数组末尾,并检查溢出情况,前提是执行此操作后,写入索引的OK值等于从索引读取的OK值)。然后将新结构写入“允许写入索引”指定的插槽。然后,您必须执行原子比较和设置操作,以将“读取自”索引设置为“允许写入索引”;如果“比较和设置”操作失败,您需要重新启动整个操作,因为另一个线程比您先更新。重复ole set()再次处理,直到比较和设置操作成功


(如果这一切听起来都是可疑的和容易出错的,那是因为它是正确的,它可以很好地实现,但是很容易实现它几乎是正确的,结果是用了99.999%的代码,然后在其他0.0001%的时间做一些令人遗憾和不可复制的事情。

我的第一个建议是,你真的应该使用互斥锁来实现它(确保每个线程持有互斥锁的时间尽可能短),看看你是否真的遇到了任何问题