我需要同步线程访问int吗?我刚刚编写了一个由多个线程并发调用的方法,我需要跟踪所有线程何时完成。代码使用这种模式:privatevoidRunReport(){_reportsRunning++;try{//运行报告的代码}finally{_reportsRunning--;这是代码中唯一更改了_reportsRunning的值并且该方法需要大约一秒钟才能运行的地方。有时,当我有超过六个左右的线程一起运行报告时,_reportsRunning的最终结果可能会下降到-1。如果我将对_runningReports++和_runningReports--的调用包装在锁中,行为似乎是正确且一致的。所以,事情是这样的:当我在C++中学习多线程时,我被教导你不需要同步调用递增和递减操作,因为它们始终是一条汇编指令,所以线程不可能在电话中间。我是否正确地教导了这一点,如果是这样,C#有什么问题?A++运算符不是C#中的原语(我怀疑它保证是C++中的原语)所以是的,您的计数受竞争条件的影响。使用Interlocked.Increment和.DecrementSystem.Threading.Interlocked.Increment(ref_reportsRunning);尝试{...}最后{System.Threading.Interlocked.Decrement(ref_reportsRunning);所以,问题是:当我+学习多线程时,我被教导你不需要同步调用递增和递减操作,因为它们总是一条汇编指令,所以一个线程不可能在电话中间。我是否正确地教授了这一点,如果是这样,C#有什么问题?这是非常错误的。在一些架构上,比如x86,有单个递增和递减指令。许多架构没有它们,需要单独加载和存储。即使在x86上,也不能保证编译器会生成这些指令的内存版本——它可能会首先加载到寄存器中,尤其是当它需要对结果执行多个操作时。即使编译器可以保证在x86上始终生成递增和递减的内存版本,仍然无法保证原始性-两个CPU可能同时修改变量并获得不一致的结果。该指令需要以锁为前缀以强制它成为原语操作——默认情况下,编译器从不发出锁变体,因为它保证操作是原语,因此性能较低。考虑以下x86汇编指令:inc[i]如果i最初为0并且代码在两个内核的两个线程上运行,则两个线程完成后的值可以合法地为1或2,因为无法保证A线程将在另一个线程完成其写入之前完成其读取,或者一个线程的写入甚至在另一个线程的读取之前可见。将其更改为:lockinc[i]将导致最终值为2。Win32的InterlockedIncrement和InterlockedDecrement以及.NET的Interlocked.Increment和Interlocked.Decrement导致执行lockinc的等效(可能是相同的机器代码)。你被教错了。确实存在具有原始面整数增量的硬件,因此您所教的内容可能适用于您当时使用的硬件和编译器。但一般来说,在C++中,您甚至不能保证增量非易失性变量在读取时会连续写入内存,更不用说从基元面上读取了。增加int是一条指令,但是如何将值加载到寄存器中呢?这就是i++的有效作用:将i加载到寄存器正如您所看到的,有3条(可能在其他平台上有所不同)指令,在任何阶段cpu都可以将上下文切换到不同的线程,从而使您的变量处于未知状态。您应该使用Interlocked.Increment和Interlocked.Decrement来解决这个问题。不,您需要同步访问权限。在Windows上,您可以使用InterlockedIncrement()和InterlockedDecrement()轻松完成此操作。我相信其他平台也有等价物。编辑:刚刚注意到C#标签。照别人说的去做。另请参阅:听说i++不是线程安全的,++i是线程安全的吗?高级语言中的任何类型的递增/递减操作(是的,与机器指令相比,即使是C语言也更高级)本质上并不是原始的。然而,每个处理器平台通常都有支持各种原语操作的原语。如果您的讲师指的是机器指令,则递增和递减操作可能是原语。然而,在当今越来越多的多核平台上,这并不总是正确的,除非它们保证一致性。高级语言通常使用低级原语机器指令来实现对原语交易的支持。这是高层API提供的一种联锁机制。x++可能不是原始的,但++x可能是(不确定,但如果你考虑后增量和前增量之间的区别,应该清楚为什么pre-更原始)。更重要的一点是,如果这些运行每次都需要一秒钟的时间,那么与方法本身的运行时间相比,锁添加的时间量将是噪音。在这种情况下尝试移除锁定可能不值得——您有一个正确的锁定解决方案,它在性能上可能与非锁定解决方案没有显着差异。在单处理器机器上,如果不使用虚拟内存,x++(忽略右值)可能会转换为x86架构上的单个primitivesINC指令(如果x很长,则该操作在使用32位编译器时只是一个原语操作)。此外,movsb/movsw/movsl是移动字节/字/长字的原始方式;编译器不喜欢将它们用作分配变量的常规方式,但您可以使用基元移动实用程序函数。虚拟内存管理器的编写方式可能是这样的,如果在写入时发生页面错误,这些指令将原样运行,但我认为通常不能保证。在多处理器机器上,除非使用明确的互锁指令(可通过特殊的库调用调用),否则所有赌注都会被取消。一般来说,最常用的命令是CompareExchange。如果该指令包含预期值,则该指令只会更改内存位置;当它决定是否改变它时,它会返回它拥有的价值。如果有人想使用1个“变量”变量,可以执行类似(在vb.net中)DimOldValueasIntegerdoOldValue=Variable和Threading.Interlocked.CompareExchange(Variable,OldValueXor1,OldValue)这种方法允许新变量应该执行任何类型的原语更新,这取决于具有旧值的变量。对于一些常见的操作,如递增和递减,有更快的替代方法,但CompareExchange还允许实现其他有用的模式。重要提示:(1)保持循环尽可能短;循环时间越长,循环中另一个任务越有可能命中该变量,每次发生时浪费的时间就越多;(2)线程之间任意划分的指定数量的更新总是会完成,因为一个线程可以强制重新执行循环的唯一方法是其他线程是否取得了有用的进展;但是,如果某些线程可以不执行更新,则代码可能会变得活锁。以上就是C#学习教程:是否需要同步线程访问int?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
