在线程原子操作中递增并返回一个整数?

Posted

技术标签:

【中文标题】在线程原子操作中递增并返回一个整数?【英文标题】:Is incrementing and returning an integer in a thread atomic operation? 【发布时间】:2015-11-22 02:21:26 【问题描述】:

考虑以下 posix 线程代码:

void *thr(void *arg)

    static int i = 0;

    return ++i;

我认为这是原子且安全的操作,因为它应该在汇编指令inc i 上进行转换以更改值。但是,我不是 cpu 和多线程内部的专家,因此我对此表示怀疑。

编辑:既然不安全说我使用互斥锁来保证安全,那么在时间方面会有多少额外成本。 编辑:我从Overhead of pthread mutexes 得到了时间成本的答案。非常感谢。

【问题讨论】:

一般来说,如果您认为某事物是线程安全的基础是您知道编译器可能生成什么汇编代码,那么——您实际上没有根据认为它是线程安全的。因为实际上,如果你的编译器做了任何类型的优化,你永远不能真正依赖你的假设。编译器做了一些非常聪明的事情来压缩一两个周期。 (但无论如何,在您的情况下,我不确定您认为 inc i 是什么。您的系统增加 register 的原语可能是原子的——但可能不是 memory location我>!) ***.com/questions/680097/… @ruakh 典型的 x86 CPU 具有用于增加内存位置的指令,但除非特别要求,否则它不是原子的。在这种情况下,没有理智的编译器可能会请求原子操作,因为它的成本要高得多,而且编译器没有理由认为它需要它。 感谢您的回答。 @DavidSchwartz 如何向编译器请求原子操作以直接增加内存中的值? 视情况而定。没有标准的 C/pthreads 方法可以做到这一点。您的编译器可能提供内在函数,您可以使用内联汇编,或者您可以使用提供它们的库。或者只是使用锁。 【参考方案1】:

由于多种原因,它既不是原子的也不是安全的:

    没有标准说它是。因此,如果碰巧是这样,那只能靠运气了,如果依赖它,你会发疯的。

    谁说一条指令是原子的?增量需要获取、添加和存储。在大多数 CPU 上,这不是原子的。 (为什么你认为 x86 CPU 提供 LOCK 前缀?如果单个指令已经是原子的,它有什么用途?)

    编译器可以用各种疯狂的方式进行优化。

但更重要的是,你犯了一个非常根本的错误。仅仅因为你想不出任何可能失败的方式,就断定某事是安全的。这永远不会奏效,你永远不应该这样做。如果你没有保证,而在这种情况下你没有保证,那是不安全的。

既然说我使用互斥锁来保证安全不安全,那么就时间而言,这将是多少额外成本。

可能很少。在大多数平台上,获取无竞争锁的成本与原子增量大致相当。因为无论如何你都需要一个原子增量,所以你必须以某种方式支付这个成本。

换句话说,在无争议的情况下(两个线程不尝试同时递增),锁定/递增/解锁将在大多数平台上执行与原子递增大致相同的操作。但是,如果有很多争用,原子增量会执行得更好。

如果您不必超级有效地处理争用,只需使用锁就可以了。如果你这样做了,那么你应该使用一些提供有效方法来处理争用的平台或库,例如带有 std::atomic 的 C++ 实现、提供内联汇编原子操作的库或具有原子内在函数的编译器。

【讨论】:

【参考方案2】:

该操作不是原子的。这意味着从内存中读取变量(它是一个静态存储的变量),将其递增,然后存储回内存中。

【讨论】:

以上是关于在线程原子操作中递增并返回一个整数?的主要内容,如果未能解决你的问题,请参考以下文章

2.原子变量 CAS算法

多线程之问题总结

原子操作 Interlocked系列函数

并发之atomic原子操作

Interlocked介绍

Interlocked介绍