c# - 局部变量需要同步吗?

标签 c# variables thread-synchronization

当多个线程同时访问数据时,必须同步访问以防止数据完整性问题。局部变量需要同步吗?

最佳答案

当多个线程同时访问数据时,必须同步访问以防止数据完整性问题。局部变量需要同步吗?


问题表明您正在以错误的方式考虑问题。一旦您开始正确地思考问题,就不再需要问这个问题了。

让我们从消除问题中的错误开始。


  当多个线程同时访问数据时,必须同步访问以防止数据完整性问题。


这是一个常见的特征,但是由于它太弱了,所以它是不正确的。表征情况的正确方法是:


可以观察到在多个线程上进行存储的读取和写入的顺序不同于每行代码的顺序执行所隐含的顺序。
不需要任何线程都遵守全局一致的顺序。关于读取是在写入之前还是之后,两个线程可能存在分歧。
由于可以观察到读写在不同的线程上以不同的顺序发生,因此事情同时发生的整个想法没有用。
C#规范限制了相对于彼此以及相对于某些其他“特殊事件”(例如,获取锁,结束线程,引发异常等)的某些种类的读写的有效重排序。有关详细信息,请参见C#规范。


因此不是:


  当多个线程同时访问数据时,必须同步访问以防止数据完整性问题。


在多线程代码的疯狂世界中,可能会出现与同时访问或数据完整性无关的问题。确切地说:


  当多个线程在内存上执行读写操作时,可以观察到这些读写处于意外顺序,并且在意外时间违反程序不变性,除非使用同步原语将有效的重排序限制为保留程序不变性的那些。


现在,您的问题的答案很明确了:


  局部变量需要同步吗?


如果必须观察到局部变量的读取和写入相对于特殊事件以特定的顺序发生以保留程序不变式,并且C#规范尚未保证所需的顺序,那么,就像对内存中的所有读取和写入一样,他们必须使用同步原语来实现所需的顺序。

不要陷入其他答案说“本地人在栈上”的陷阱-它们不一定在短期内!仅当本地对象的生存期短于方法的激活时,并且逻辑上将方法激活形成堆栈时,才在短期池中分配它们。作为lambda的封闭本地变量的本地变量具有较长的生存期,并且迭代器块或异步方法中的本地变量处于其激活未形成堆栈的方法中。

此外,谁在乎?堆栈内存可以使用不安全的代码在线程之间共享。

也不要陷入认为某些本地人是“真实本地人”而有些人是“假本地人”的陷阱。本地的定义特征是其名称具有本地范围,而不是生存期短。不保证本地人在C#中的寿命很短。

以下是可以在多个线程上访问本地的一些方法:


本地位于迭代器块中; IEnumerator可以在多个线程上访问。注意,这几乎肯定会可怕地死掉。在这些情况下,迭代器块的设计不安全。
本地位于异步方法中,该方法在等待的线程之外的其他线程上恢复。尽管这不涉及“同时”访问,但是谁在乎呢?基本的问题不是是否可以同时访问本地,而是相对于特殊事件,是否观察到本地的读取和写入处于特定顺序。
本地是lambda或匿名方法的封闭本地;委托(或编译的表达式树)可以在多个线程上调用。谨慎使用!使用任务并行库很容易做到这一点。
本地是非托管类型;指向本地的指针将传递到多个线程,这些线程现在可以对其进行读取或写入。即使本地“在堆栈上”,也会发生这种情况。当然,当您使用不安全的代码时,安全系统将关闭,您有责任确保安全。这就是为什么它被称为“不安全”的原因。

关于c# - 局部变量需要同步吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47760767/

相关文章:

c# - 在 IIS 服务器上缓存大图像的最佳方法

ios - 如何在 iOS SDK 中使用 presentViewController 在 View Controller 之间传递变量?

java - 我的值一直显示为 NaN

c++ - 现实生活中是否存在简单的 bool 指针作为线程取消标志不会有效取消线程的情况?

c# - ASP.NET MVC 基于用户角色显示 HTML

c# - Entity Framework SaveChanges 两种行为取决于我如何添加到 DbContext

c# - 如何在属性更改时触发 DataTemplateSelector?

variables - 将伴侣(婴儿)变量的值复制到原始(母亲)

java - 如何让线程等待另一个类的方法完成

c# - 阻止函数并等待事件 C#