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)
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的InterlockedIncrement和InterlockedDecrement与.NET的Interlocked.Increment,并Interlocked.Decrement导致这样的等价物(可能是完全一样的机器代码)lock inc.