【问题标题】:i++ thread safei++ 线程安全
【发布时间】:2011-08-01 06:12:30
【问题描述】:

这是对this question 的补充,我可以得出结论,在c/c++ 中,这样的操作不是线程安全的。

我的问题是在线程安全方面我们需要在任何情况下获取吗?注意这里锁是一个逻辑概念,即使你使用 InterlockedIncrement() 或 c++0x 原子类型,锁在概念上是使用 cmpxchg 获取的。

例如,如果只有一个写线程和多个读线程,读线程会得到奇怪的值吗?我假设

  1. 类型 i 在 x86 平台上为 32 位,在 x64 平台上为 64 位。
  2. 旧值或新值都可以。

【问题讨论】:

  • 注意:我可以定义为 int32,它会确保它是一个 32 位的值
  • 请编辑链接以引用合法网址

标签: c++ c multithreading


【解决方案1】:

在单个写入器和多个读取器的情况下,它是线程安全的,但在一般情况下,此类操作不是线程安全的(因此需要锁或使用原子操作)。另外,这里 thread-safe 的含义非常有限。

如果您只是在一个线程中执行i++,其他线程将看到旧值或新值。在您提到的两个平台上,这些值是原子存储/加载的,因此它们无法获得一半的值。然而,这通常不是真的,例如 x86 上的 64 位值不会是原子的,因此读者可以获得旧值的一半和新值的一半。所以这里的 线程安全 是非常特定于平台的。

但是,您仍然必须小心。如果这是一个普通的int,优化器可能会简单地丢弃加载操作(也许在寄存器中保留一个副本)。在这种情况下,读者永远不会得到新的价值。如果您在循环中执行此操作,这一点至关重要。不幸的是,唯一正确的标准方法是使用 C++0x 使用 atomic<T> 类型(volatile 类型现在为某些编译器服务)。

如果您确实添加了第二个写入器,则增量运算符当然根本不是线程安全的。但是,您可以在此处使用原子添加函数,使其再次成为线程安全的。

【讨论】:

  • 感谢您的回复,所以,如果我添加第二个写线程,在​​这种情况下,我只能在写线程之间获取锁吗?我们还假设 i 匹配机器字长,即 x86 平台上的 32 位或 x64 平台上的 64 位。并且读取线程可以获取旧值或新值,而不是奇怪的值(即半更新),对吧?
  • volatile 不会像您所想的那样做,因为这里已经讨论了一百万次。你也没有提到对齐,这是一个重要的遗漏。
  • @chang 没错,但互锁的无锁方法会更好
  • @David,请参阅我的问题stackoverflow.com/questions/5579782/… 以了解我为什么说 volatile kind of SERVICE the purpose (在 C++0x 之前,C1X 是您对原子的唯一选择)
  • @David,是的,变量必须正确对齐。但是,如果不进行强制转换,您的变量通常会正确对齐。
【解决方案2】:

如果您可以访问 Qt,请查看他们的 QAtomicInt 课程。它是完全独立的,有可能(我设法做到了)从那里提取所有必要的东西来拥有一个独立的便携式 atomic_int 类。

它提供原子fetch_and_storefetch_and_addcompare_and_swapincrementdecrement 与屏障语义(获取、释放、完全、无屏障),尽管在 x86 上每个操作都将是完全屏障。

还有一个QAtomicPointer 类模板。

【讨论】:

    猜你喜欢
    • 2010-10-15
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 2021-07-12
    • 2011-03-30
    • 1970-01-01
    相关资源
    最近更新 更多