我是否需要同步对int的线程访问?

Mar*_*ris 20 c# c++ multithreading locking

我刚刚编写了一个由多个线程同时调用的方法,我需要跟踪所有线程何时完成.代码使用此模式:

private void RunReport()
{
   _reportsRunning++;

   try
   {
       //code to run the report
   }
   finally
   {
       _reportsRunning--;
   }
}
Run Code Online (Sandbox Code Playgroud)

这是代码中唯一_reportsRunning值被更改的地方,该方法需要大约一秒钟才能运行.

偶尔当我有超过六个左右的线程一起运行报告时,_reportsRunning的最终结果可以降到-1.如果我换到电话_runningReports++_runningReports--一个锁,则行为似乎是正确的,一贯的.

所以,问题是:当我在C++中学习多线程时,我被教导你不需要同步调用递增和递减操作,因为它们总是一个汇编指令,因此线程不可能在中间切换-呼叫.我是否正确地教过,如果是这样的话,那对C#来说怎么回事?

Hen*_*man 27

一个++经营者不得在C#中的原子(我怀疑是保证在C++原子)所以是的,你的票是受竞争条件.

使用Interlocked.Increment和.Decrement

System.Threading.Interlocked.Increment(ref _reportsRunning);
try 
{
  ...
}
finally
{
   System.Threading.Interlocked.Decrement(ref _reportsRunning);
}
Run Code Online (Sandbox Code Playgroud)

  • C++没有指定任何原子操作. (5认同)
  • ++在C++中也不是原子的. (3认同)

Mic*_*ael 20

所以,问题是:当我在C++中学习多线程时,我被教导你不需要同步调用递增和递减操作,因为它们总是一个汇编指令,因此线程不可能在中间切换-呼叫.我是否正确地教过,如果是这样的话怎么会对C#不适用?

这是非常错误的.

在某些体系结构(如x86)上,有单个递增和递减指令.许多架构没有它们,需要单独加载和存储.即使在x86上,也无法保证编译器会生成这些指令的内存版本 - 它可能首先加载到寄存器中,特别是如果它需要对结果执行多个操作.

即使编译器可以保证始终在x86上生成递增和递减的内存版本,但仍然不能保证原子性 - 两个CPU可以同时修改变量并获得不一致的结果.该指令需要锁定前缀以强制它为原子操作 - 编译器默认情况下从不发出锁定变量,因为它的性能较低,因为它保证了操作是原子的.

请考虑以下x86汇编指令:

inc [i]
Run Code Online (Sandbox Code Playgroud)

如果我最初为0并且代码在两个核心上的两个线程上运行,则两个线程完成后的值可以合法地为1或2,因为无法保证一个线程将在另一个线程完成其写入之前完成其读取,或者在其他线程读取之前,一个线程的写入甚至可见.

将此更改为:

lock inc [i]
Run Code Online (Sandbox Code Playgroud)

将导致最终值为2.

的Win32的InterlockedIncrementInterlockedDecrement与.NET的Interlocked.Increment,并Interlocked.Decrement导致这样的等价物(可能是完全一样的机器代码)lock inc.