C# 我需要同步对int的线程访问吗?

C# 我需要同步对int的线程访问吗?,c#,c++,multithreading,locking,C#,C++,Multithreading,Locking,我刚刚编写了一个由多个线程同时调用的方法,我需要跟踪所有线程何时完成。代码使用以下模式: private void RunReport() { _reportsRunning++; try { //code to run the report } finally { _reportsRunning--; } } 这是代码中唯一一个更改了\u reportsRunning值的位置,该方法运行大约需要一秒钟 有时,当我有六个以

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

private void RunReport()
{
   _reportsRunning++;

   try
   {
       //code to run the report
   }
   finally
   {
       _reportsRunning--;
   }
}
这是代码中唯一一个更改了
\u reportsRunning
值的位置,该方法运行大约需要一秒钟

有时,当我有六个以上的线程一起运行报告时,运行报告的最终结果可以降到-1。如果我将对
\u runningReports++
\u runningReports--
的调用包装在一个锁中,那么行为似乎是正确和一致的


这样的问题:当我在C++中学习多线程时,我被告知,你不需要同步调用增量和减量运算,因为它们总是一个汇编指令,因此线程不可能在中间调用。我是否被正确地教导过,如果是的话,为什么这对C#不成立?

A
++
运算符在C#中不是原子的(我怀疑它在C++中是否保证是原子的),所以是的,您的计数取决于竞争条件

使用联锁的增量和减量

System.Threading.Interlocked.Increment(ref _reportsRunning);
try 
{
  ...
}
finally
{
   System.Threading.Interlocked.Decrement(ref _reportsRunning);
}

否,您需要同步访问。在Windows上,可以使用InterlockedIncrement()和InterlockedIncrement()轻松完成此操作。我相信其他平台也有类似的平台

编辑:刚刚注意到C#标记。照另一个人说的做。另请参见:

您被教错了


确实存在具有原子整数增量的硬件,因此您所学习的内容可能适用于您当时使用的硬件和编译器。但一般来说,在C++中,你甚至不能保证增加一个非易失变量,通过读取它来连续地写入内存,更不用说用原子方式来读取了。

递增<代码> int <代码>是一个指令,但是在登记器中加载值呢? 这就是
i++
有效地做到的:

  • i加载到寄存器中
    • 递增寄存器
    • 将寄存器卸载到i
  • 如您所见,有3条指令(在其他平台上可能有所不同),在任何阶段,cpu都可以将上下文切换到不同的线程,使变量处于未知状态

    你应该使用和来解决这个问题

    所以,问题是:当我 C++中多线程的学习 告诉我你不需要 同步对增量和 减量操作,因为它们是 始终有一个装配说明和 因此,这是不可能的 线程在调用过程中被切换出去。 我是否被正确地教导,如果是,如何教导 来吧,这对C不成立

    这是非常错误的

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

    即使编译器可以保证总是在x86上生成增量和减量的内存版本,这仍然不能保证原子性——两个CPU可能同时修改变量并得到不一致的结果。指令将需要锁前缀来强制它成为原子操作-编译器默认情况下不会发出锁变量,因为它保证操作是原子的,所以性能较低

    考虑以下x86汇编指令:

    inc [i]
    
    如果I最初为0,并且代码在两个内核上的两个线程上运行,那么两个线程完成后的值在法律上可以是1或2,因为不能保证一个线程将在另一个线程完成其写入之前完成其读取,或者一个线程的写入甚至在其他线程读取之前可见

    将此更改为:

    lock inc [i]
    
    将导致最终值为2


    Win32的
    InterlockedIncrement
    InterlockedDecrement
    和.NET的
    Interlocked.Increment
    Interlocked.Decrement
    导致在更高级别的语言中执行与
    lock inc
    任何类型的递增/递减操作等效的(可能是完全相同的机器代码)(是的,与机器指令相比,即使是
    C
    也是更高级别的)本质上也不是原子的。然而,每个处理器平台通常都有


    如果你的讲师所指的是机器指令,那么递增和递减操作很可能是原子操作。然而,在当今不断增长的多核平台上,这并不总是正确的,除非它们能保证


    高级语言通常使用低级原子机器指令实现。这是高级API提供的互锁机制。

    x++可能不是原子的,但++x可能是原子的(不确定),但如果考虑到前后增量之间的差异,那么应该清楚为什么预处理更适合原子性。 更重要的一点是,如果每次运行都需要一秒钟的时间,那么与方法本身的运行时间相比,锁所增加的时间量将是噪音。在这种情况下,尝试移除锁可能不值得玩弄——你已经找到了一个正确的锁解决方案,这在性能上可能不会有明显的差异来自非锁定解决方案的ce。

    在单处理器计算机上,如果不使用虚拟内存,x++(忽略右值)可能会转换为x86体系结构上的单个atomic INC指令(如果x很长,则在使用32位编译器时,操作仅为原子操作)。此外,movsb/movsw/movsl是移动字节/字/长字的原子方式;编译器不倾向于将其作为正常方式使用 Dim OldValue as Integer Do OldValue = Variable While Threading.Interlocked.CompareExchange(Variable, OldValue Xor 1, OldValue) OldValue