6

我很清楚以前曾问过类似的问题,而且我也知道,该操作很可能根本不是原子的,但我仍然出于好奇而询问,并希望有某种方法可以使它成为原子的.

情况:在 astruct中,有一个 unsigned char 变量,称为Busy. (它可以移出那里并独立站立)。

此变量Busy由两个并发线程修改,一个在调度时设置位,另一个在计划操作完成时清除它们。

现在,调度看起来像这样:

while(SEC.Busy&(1 << SEC.ReqID))
    if(++SEC.ReqID == 5) SEC.ReqID = 0;
sQuery.cData[2] = SEC.ReqID;

而位图的清除看起来像这样:

SEC.Busy &= ~(1 << sQuery->cData[2]);

cData[2] 基本上携带有关在网络上使用哪个插槽的信息,并通过另一个线程中的回调返回。

现在的问题是:我怎样才能确保 SEC.Busy (这是这种麻烦情况下的唯一变量)不会被两个试图在不使用互斥锁、临界区或任何东西的情况下同时更改它的线程撕裂如果可能的话,喜欢?

我也尝试将 SEC.Busy 的内容分配给一个局部变量,改变它然后写回变量,但不幸的是这个操作也不是原子的。

我目前正在使用 Borland C++ Builder 6,尽管 GCC 解决方案也可以。

非常感谢你。

4

2 回答 2

6

C++03(也不是 C99)根本没有提到原子性。在大多数平台上,赋值是原子的(= 每个人都看到旧值或新值),但因为它不是同步的(= 任何人在看到其他更新的新值后都可能看到旧值),所以无论如何它是无用的。任何其他操作,如增量、设置位等可能甚至都不是原子的。

C++11 定义了std::atomictemplate,保证了原子性和同步性,所以需要用到。Boost 为大多数 C++03 编译器提供了兼容的实现,并且 gcc从 4.2 开始就有内置支持,它正在被gcc 4.7 中 C++11 所需的更高级支持所取代

Windows API很久以前就有“互锁操作” 。在引入 gcc__sync函数之前,Unix 替代所需的程序集(有几个库提供)。

于 2013-06-03T09:10:38.930 回答
1

从多个线程访问共享数据时存在三个潜在问题。首先,您可能会在需要多个总线周期的内存访问过程中遇到线程切换;这被称为“撕裂”。其次,每个处理器都有自己的内存缓存,将数据写入一个缓存不会自动写入其他缓存,因此不同的线程可以看到陈旧的数据。第三,编译器可以移动指令,这样另一个处理器可能会看到稍后存储到一个变量,而不会看到之前存储到另一个变量。

使用类型变量unsigned char几乎可以肯定会删除第一个变量,但对其他两个变量没有任何影响。

为避免这三个问题,请使用atomic<unsigned char>C++11 或您的编译器和操作系统提供的任何同步技术。

于 2013-06-03T17:40:52.413 回答