局部变量是否需要同步?

时间:2017-12-11 20:20:01

标签: c# variables thread-synchronization

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

2 个答案:

答案 0 :(得分:5)

  

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

问题表明你以错误的方式思考问题。一旦你开始正确地思考问题,你就不再需要提问了。

首先删除问题中的错误。

  

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

这是一种常见的特征,但它太巧妙了,因为它太弱了。描述情况的正确方法是:

  • 可以观察到多个线程上的存储的读取和写入以不同于每行代码的顺序执行所暗示的顺序的顺序发生。
  • 不要求任何线程遵守全局一致的排序。两个线程可能不同意读取是在写入之前还是之后。
  • 由于可以观察到读取和写入在不同的线程上以不同的顺序发生,事情同时发生的整个想法是无用的
  • C#规范限制某些类型的读写相对于彼此的有效重新排序,以及某些其他特殊事件和#34;例如取一个锁,结束一个线程,抛出一个异常,等等。有关详细信息,请参阅C#规范。

所以不是:

  

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

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

  

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

现在你的问题的答案很明确:

  

是否需要同步局部变量?

如果必须遵守关于特殊事件的特定顺序的局部变量的读写,以保留程序不变量,并且C#规范尚未保证所需的顺序然后,就像所有从内存中读取和写入一样,它们必须使用同步原语来实现所需的顺序。

不要陷入其他一些答案所说的“当地人在堆栈上”的陷阱。 - 他们不一定是短期游泳池!只有在短期池的生命周期短于方法的激活时才会分配本地,而当方法激活在逻辑上形成堆栈时。作为lambda的封闭本地的本地生命周期很长,迭代器块或异步方法中的locals在激活不形成堆栈的方法中。

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

也不要陷入认为某些当地人是真正的当地人和#34;还有一些是假冒的当地人"。本地的定义特征是其名称具有本地范围,而不是它具有短生存期。不保证当地人在C#中的寿命很短。

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

  • 本地处于迭代器块中;可以在多个线程上访问IEnumerator。请注意,这几乎肯定会死得很厉害;在这些条件下,迭代器块的设计并不安全。
  • 本地处于异步方法中,该方法在等待的线程以外的线程上恢复。虽然这不涉及访问"同时",谁在乎?根本问题不在于是否可以同时访问本地,而是观察到本地的读写是否与特殊事件有关。
  • 本地是一个lambda或匿名方法的封闭本地;可以在多个线程上调用委托(或编译的表达式树)。谨慎使用!使用任务并行库很容易。
  • 本地是一种非管理型;指向本地的指针被传递给多个线程,现在可以读取或写入它。 即使本地是#34;" ,也会发生这种情况。当然,当您使用不安全的代码时,安全系统关闭,您负责确保安全。这就是为什么它被称为"不安全"。

答案 1 :(得分:0)

是的,如果并行访问:

private List<int> GetInts()
{
    var res = new List<int>();
    Parallel.For(0,100, i=>
    {
        lock(res)
        {
            res.Add(i);
        }
    });
    return res;
}