【问题标题】:C++ multithreading problem - is mutex the only way?C ++多线程问题-互斥锁是唯一的方法吗?
【发布时间】:2010-01-28 12:50:20
【问题描述】:

我有一个计数器变量,它将被多个线程访问,这将增加/减少它。它不应该被多个线程同时更新。

我知道您可以创建一个互斥对象,必须在更改相关变量之前获取该对象。在这种情况下,临界区是不合适的,因为有超过 1 个函数可以更改相关变量。

还有其他我可以在不使用互斥锁的情况下做到这一点吗?使用互斥体确实会降低性能(请参阅http://www.codeguru.com/forum/showthread.php?t=333192)。我相信在Java中,你可以在变量声明中使用一个关键字来实现这一点(它是否称为“同步”?),但是在C++中有这样的东西吗?

我知道 volatile 不是我要查找的关键字。

非常感谢。

【问题讨论】:

  • 临界区仍然可以在这里工作,只要计数器只能由单个进程访问。它可以由具有相同进程的多个功能使用。事实上,临界区比互斥快。
  • re Java:不,synchronized在java中相当于一个临界区,与volatile和原子递增/递减不同。

标签: java c++ synchronization multithreading synchronisation


【解决方案1】:

大多数处理器都有“atomic”递增和递减指令 - 在很大程度上,它们是在机器级别实现互斥锁的方式。

您可以在自己的代码中访问这些原子指令。 Windows 提供了InterlockedIncrement() 函数和glib provides equivalents。 x86汇编语言中,可以直接使用LOCK CMPXCHG和kin。

C++ 对这些概念一无所知——你必须自己使用它们; C++ 中没有用于线程安全的神奇关键字。

Atomic Instruction

【讨论】:

    【解决方案2】:

    虽然使用原子操作可能是最有效的,但在多个函数中使用 this 的事实并不妨碍在此代码或任何其他代码中使用关键部分 - 只需编写一个函数:

    void IncDec( bool inc ) {
       EnterCritical Section( theCS );
       if ( inc ) {
         theVar++;
       }
       else {
         thevar--;
       }
       LeaveCriticalSection( theCS );
    }
    

    并从您的其他函数中调用它。

    【讨论】:

    【解决方案3】:

    从描述看来,您可能只需要InterlockedIncrement 和相关的减量函数。

    编辑 - 这些是 Windows 功能...我没有停下来询问哪个平台。

    【讨论】:

    • 那么你可以在变量声明之前放置任何东西来指定它必须以原子方式访问吗?我知道 const、mutable 和 volatile 是 C++ 限定词,但它们不符合要求。
    • @Andy 不,没有。当前的 C++ 标准根本不涉及线程。
    【解决方案4】:

    在这种情况下,关键部分是 不合适,因为有更多 比 1 可以改变的功能 有问题的变量。

    这是使用关键部分的常见场景,您需要确保访问变量的每一段代码在进入相同的关键部分(或互斥体,或使用任何保护)时都这样做。

    【讨论】:

      【解决方案5】:

      在 Win32 中,IntelockedIncrement/IntelockedIncrement64 和相关操作编译为 x86 指令,允许对 32 位或 64 位字(取决于您的体系结构)进行处理器级原子操作。这在简单计数器的情况下可以正常工作,但如果您尝试将较大的结构与多个单词同步,自然就无法正常工作。

      PS 来自here,您需要在 x86 上运行的非 Win32 系统上实现它的相应 asm。

      inline long InterlockedExchangeAdd( long* Addend, long Increment )
      {
      long ret;
      __asm__ (
      /* lock for SMP systems */
      "lock\n\t"
      "xaddl %0,(%1)"
      :"=r" (ret)
      :"r" (Addend), "0" (Increment)
      :"memory" );
      return ret;
      }
      
      inline long InterlockedIncrement( long* Addend )
      {
      return InterlockedExchangeAdd( Addend, 1 );
      }
      
      inline long InterlockedDecrement( long* Addend )
      {
      return InterlockedExchangeAdd( Addend, -1 );
      }
      

      【讨论】:

        【解决方案6】:

        您可以对计数器变量使用原子类型 - 例如 sig_atomic_t(在 GNU libc 中)。那么就不需要同步了,因为不会发生竞态条件,所以保证对这个变量的操作是原子的。

        【讨论】:

        【解决方案7】:

        正如其他人所提到的,当前的 C++ 标准中没有任何内容支持原子变量访问。但是,如果您要求 C++ 库支持(我并不完全清楚),正在尝试模仿即将到来的 C++ 标准的原子支持here

        【讨论】:

        • 谢谢。 C++ 确实有一些东西可以使变量成为线程的局部变量 - declspec(thread) 但它可能特定于 Windows 平台。
        • @Andy: __declspec(thread) 使变量线程仿射——这意味着它们不能在线程之间共享(好吧,至少在不违反它们的目的的情况下是这样)。
        • @Andy:哦,而且 __declspec(thread) 不是特定于 Windows 的,它是特定于 VC++ 的。
        【解决方案8】:

        这种情况下的临界区是不合适的,因为有超过 1 个函数可以更改相关变量。

        如果您不使用关键部分创建单个函数来修改变量,您如何知道您已找到该变量可能访问的所有位置?

        此外,您需要声明变量 volatile 以确保您的编译器不会无意中优化对变量的任何访问。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多