什么时候可以保证64位写入是原子的,在基于Intel x86的平台上用C编程时(特别是使用英特尔编译器运行MacOSX 10.4的基于Intel的Mac)? 例如:

unsigned long long int y;
y = 0xfedcba87654321ULL;
/* ... a bunch of other time-consuming stuff happens... */
y = 0x12345678abcdefULL;

如果另一个线程在y的第一次赋值完成后检查y的值,我想确保它看到值0xfedcba87654321或值0x12345678abcdef,而不是它们的某些混合。 我想这样做没有任何锁定,如果可能的话没有任何额外的代码。 我希望,当在支持64位代码(MacOSX 10.4)的操作系统上使用64位编译器(64位Intel编译器)时,这些64位写入将是原子的。 这总是如此吗?

===============>>#1 票数:42

最好的办法是避免尝试用原语构建自己的系统,而是使用锁定,除非它在分析时真的显示为热点。 (如果你认为你可以聪明并且避免锁定,那就不要。你不是。那是包括我和其他人在内的一般“你”。)你应该至少使用自旋锁,参见spinlock(3) 无论你做什么, 都不要试图实现“你自己的”锁。 你会弄错的。

最终,您需要使用操作系统提供的任何锁定或原子操作。 所有情况下 完全正确地获取这些东西是非常困难的 通常它可能涉及特定处理器的特定版本的勘误表之类的知识。 (“哦,该处理器的2.0版本没有在正确的时间执行缓存一致性窥探,它在2.0.1版本中修复,但在2.0版本中需要插入NOP 。”)只需在变量上打一个volatile关键字在C中几乎总是不够。

在Mac OS X上,这意味着您需要使用atomic(3)中列出的函数对32位,64位和指针大小的数量执行真正的原子跨所有CPU操作。 (使用后者对指针进行任何原子操作,这样你就可以自动进行32/64位兼容。)无论你是想做原子比较和交换,递增/递减,自旋锁定还是堆栈/队列,都会这样做。管理。 幸运的是, spinlock(3)atomic(3)barrier(3)函数应该在Mac OS X支持的所有CPU上都能正常工作。

===============>>#2 票数:13

在x86_64上,Intel编译器和gcc都支持一些内部原子操作函数。 这是gcc的文档: http//gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html

英特尔编译器文档也在这里讨论它们: http//softwarecommunity.intel.com/isn/downloads/softwareproducts/pdfs/347603.pdf (第164页左右)。

===============>>#3 票数:12

根据英特尔处理器手册第3A部分 - 系统编程指南第7章,如果在64位边界,Pentium或更高版本上对齐,并且未对齐(如果仍在高速缓存行内),则将以原子方式执行四字访问。 P6或更新。 您应该使用volatile来确保编译器不会尝试将写入缓存在变量中,并且您可能需要使用内存栅栏例程来确保以正确的顺序执行写入。

如果需要将写入现有值的值作为基础,则应使用操作系统的Interlocked功能(例如,Windows具有InterlockedIncrement64)。

===============>>#4 票数:10

在Intel MacOSX上,您可以使用内置系统原子操作。 没有为32位或64位整数提供原子获取或设置,但您可以使用提供的CompareAndSwap构建它。 您可能希望在XCode文档中搜索各种OSAtomic功能。 我在下面写了64位版本。 32位版本可以使用类似命名的函数完成。

#include <libkern/OSAtomic.h>
// bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue, int64_t newValue, int64_t *theValue);

void AtomicSet(uint64_t *target, uint64_t new_value)
{
    while (true)
    {
        uint64_t old_value = *target;
        if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, target)) return;
    }
}

uint64_t AtomicGet(uint64_t *target)
{
    while (true)
    {
        int64 value = *target;
        if (OSAtomicCompareAndSwap64Barrier(value, value, target)) return value;
    }
}

请注意,Apple的OSAtomicCompareAndSwap函数以原子方式执行操作:

if (*theValue != oldValue) return false;
*theValue = newValue;
return true;

我们在上面的例子中使用它来创建一个Set方法,首先获取旧值,然后尝试交换目标内存的值。 如果交换成功,则表示内存的值仍然是交换时的旧值,并且在交换期间给出了新值(它本身是原子的),所以我们完成了。 如果它没有成功,那么其他一些线程通过在我们抓住它和我们试图重置它时修改其间的值来干扰。 如果发生这种情况,我们可以简单地循环并再次尝试,只有最小的惩罚。

Get方法背后的想法是我们可以先获取值(如果另一个线程正在干扰,则可能是也可能不是实际值)。 然后我们可以尝试将值与自身交换,只需检查初始抓取是否等于原子值。

我没有对我的编译器进行检查,所以请原谅任何错别字。

您特别提到了OSX,但是如果您需要在其他平台上工作,Windows有许多Interlocked *功能,您可以在MSDN文档中搜索它们。 其中一些适用于Windows 2000 Pro及更高版本,而一些(特别是一些64位功能)是Vista的新功能。 在其他平台上,GCC 4.1及更高版本具有各种__sync *函数,例如__sync_fetch_and_add()。 对于其他系统,您可能需要使用程序集,您可以在src / system / libroot / os / arch中的HaikuOS项目的SVN浏览器中找到一些实现。

===============>>#5 票数:6

在X86上,原子地写入对齐的64位值的最快方法是使用FISTP。 对于未对齐的值,您需要使用CAS2(_InterlockedExchange64)。 由于BUSLOCK,CAS2操作非常慢,因此通常可以更快地检查对齐并为对齐的地址执行FISTP版本。 实际上,这就是Intel Threaded构建模块实现Atomic 64位写入的方式。

===============>>#6 票数:3

最新版本的ISO C(C11)定义了一组原子操作,包括atomic_store(_explicit) 有关详细信息,请参阅此页面

原子的第二个最便携的实现是GCC内在函数,已经提到过。 我发现它们得到了GCC,Clang,Intel和IBM编译器的全面支持,并且 - 在我最后一次检查时 - 得到了Cray编译器的部分支持。

C11原子的一个明显优势 - 除了整个ISO标准之外 - 是它们支持更精确的记忆一致性处方。 据我所知,GCC原​​子意味着一个完整的记忆障碍。

===============>>#7 票数:2

如果你想为interthread或进程间通信做这样的事情,那么你需要的不仅仅是原子读/写保证。 在您的示例中,您似乎希望写入的值表示某些工作正在进行和/或已完成。 您需要做几件事,并非所有事情都是可移植的,以确保编译器按照您希望的顺序完成任务(volatile关键字可能在某种程度上有所帮助)并且内存是一致的。 现代处理器和缓存可以在编译器不知情的情况下执行无序工作,因此您确实需要一些平台支持(即,锁或特定于平台的互锁API)来执行您想要执行的操作。

“记忆围栏”或“记忆障碍”是您可能想要研究的术语。

===============>>#8 票数:1

GCC具有原子操作的内在函数; 我怀疑你也可以和其他编译器做类似的事情。 永远不要依赖编译器进行原子操作; 除非你明确地告诉编译器不要这样做,否则优化几乎肯定会冒着将显然原子操作变成非原子操作的风险。

  ask by community wiki translate from so

未解决问题?本站智能推荐:

1回复

中频的原子性及以下陈述

这是《 A. Williams C ++并发操作手册》,无锁堆栈,带有危险指针主题。 假设我对它有所了解,除了只有两行,它们是(这里的源代码 ): 问题是:另一个线程能否在取消引用(old_head-> data)之前删除(修改等)“ old_head”指针? 逻辑上说威廉姆斯
4回复

有没有办法让两个读取原子?

我遇到了需要内存中两个值的原子总和的情况。 我继承的代码是这样的: a和b的单独读取是原子的,并且代码中其他位置写入这两个存储器位置的所有内容也是无锁原子的。 然而,问题在于两个位置的值可以并且确实在两个读取之间改变。 那么如何使这个操作成为原子? 我知道关于CAS的所有内容
2回复

竞争原子操作可以相互挨饿吗?

想象一下有两个线程的程序。 它们运行以下代码(CAS指比较和交换 ): 线程B是否有可能永远导致线程A的CAS失败,从而永远不会将0xdeadbeef写入'test'? 或者自然调度抖动是否意味着在实践中这种情况永远不会发生? 如果在线程A的while循环中完成了一些工作怎么办?
3回复

无锁双链表的原子操作

我根据这些文件编写了无锁双向链表: “基于引用计数的高效可靠的无锁内存回收”,Anders Gidenstam,成员,IEEE,Marina Papatriantafilou,H˚akan Sundell和Philippas Tsigas “无锁双端队列和双链表”,Philippas
1回复

用Java创建原子RingBuffer

我在考虑如何在Java和Android中实现线程安全的RingBuffer(由于某些原因,即使经过了这么多年,也没有循环队列。因此,没有(Circular / Ring)ByteBuffer,也没有(Circular / Ring)(缓冲区/队列)。 甚至大多数第三方的RingBuffer
1回复

无锁编程:原子值有多新鲜?

一个原子变量在多个同时运行的线程之间共享。 据我所知,线程可以读取过时的值来执行轻松的加载: x.load(std::memory_order_relaxed) 如果我们使用发布获取同步怎么办? 据我所知,执行获取的线程可以确保查看释放线程已将哪些内容写入变量。 // Thre
1回复

C ++ Treiber Stack和原子下一个指针

“ Treiber Stack ”通常是最简单的无锁数据结构之一,因此在教授无锁算法的介绍时经常使用它。 我已经看到许多使用C ++原子的Treiber Stacks的实现。 算法本身很简单,所以真正的挑战是处理无锁数据结构的所有其他附带细节,例如提供一些执行安全内存回收的方法,避免AB
4回复

随着更多CPU的添加,原子操作会变慢吗?

x86和其他体系结构提供了特殊的原子指令(lock,cmpxchg等),允许您编写“无锁”数据结构。 但随着越来越多的内核被添加,似乎这些指令实际上必须在幕后进行的工作将会增长(至少是为了保持缓存一致性?)。 如果原子添加在双核系统上今天需要大约100个周期,那么未来的80多个核心机器上可能
1回复

C ++中多值的多线程原子存储/加载

假设我在C ++中有一个结构和类: 我将有R个读者线程和一个作家线程。 highest_*线程将更新零个或多个highest_*成员。 如果读取器线程调用get_highs() ,则在读取器线程读取highest_x , highest_y等产生矢量之前 ,我需要写入器线程的push
1回复

C ++:如果std :: atomic_flag是唯一的无锁原子类型,那么如何在C ++中实现无锁数据结构?

实现无锁数据结构的典型方法是使用原子CAS操作,例如std::compare_exchange_strong或std::compare_exchange_weak 。 这种技术的用法示例可以在Antony Williams的“C ++ Concurrency in Action”中看到,其中实