我很清楚之前已经问过类似的问题而且我也知道,这个操作很可能根本就不是原子的,但我仍然要求空闲的好奇心,并希望有一些方法可以让它成为原子。
情况:在struct
内,有一个名为Busy
的无符号字符变量。 (虽然它可以移出那里然后独立)。
此变量Busy
由两个并发线程修改,一个在调度时设置位,另一个在完成调度操作后清除它们。
现在,日程安排如下:
while(SEC.Busy&(1 << SEC.ReqID))
if(++SEC.ReqID == 5) SEC.ReqID = 0;
sQuery.cData[2] = SEC.ReqID;
虽然清除bitmaks看起来像这样:
SEC.Busy &= ~(1 << sQuery->cData[2]);
cData [2]基本上包含有关通过网络使用哪个插槽的信息,并通过另一个线程中的回调返回。
现在的问题是:我怎样才能确保SEC.Busy(在这种麻烦的情况下唯一的变量)不会被两个线程同时试图改变它而不使用互斥锁而被撕裂部分或任何喜欢的人如果可能的话?
我也尝试将SEC.Busy的内容分配给一个局部变量,改变它然后再写回变量,但不幸的是这个操作似乎也不是原子的。
我目前正在使用Borland C ++ Builder 6,不过GCC解决方案也可以。
非常感谢。
答案 0 :(得分:6)
C ++ 03(也不是C99)根本没有说明原子性。在大多数平台上,分配是原子的(=每个人都看到旧值或新值),但因为它没有同步(=任何人在看到其他更新的新值后可能会看到旧值)无论如何,它无用。任何其他操作,如增量,设置位等都可能不是原子的。
C ++ 11定义std::atomic
template,它确保原子性和同步,因此您需要使用它。 Boost为大多数C ++ 03编译器和gcc has had built-in support since 4.2提供了兼容的实现,它被more advanced support needed by C++11 in gcc 4.7取代
Windows API早就有了"Interlocked operations"。在引入gcc __sync
函数之前,Unix替代需要汇编(几个库提供)。
答案 1 :(得分:1)
从多个线程访问共享数据时存在三个潜在问题。首先,您可能会在内存访问过程中获得一个需要多个总线周期的线程切换;这被称为“撕裂”。其次,每个处理器都有自己的内存缓存,将数据写入一个缓存不会自动写入其他缓存,因此不同的线程可以看到过时的数据。第三,编译器可以移动指令,以便另一个处理器可以看到稍后存储到一个变量而不会看到前一个存储到另一个存储。
使用unsigned char
类型的变量几乎肯定会删除第一个变量,但它对其他两个变量没有任何影响。
要避免所有这三个问题,请使用C ++ 11中的atomic<unsigned char>
或编译器和操作系统提供的任何同步技术。