【问题标题】:.NET C# Multithreading.NET C# 多线程
【发布时间】:2010-11-27 01:39:50
【问题描述】:

关于线程以满足我的好奇心的问题......

假设我有静态变量 _status (ProgressStatus) 并且许多线程正在从中读取。 为了更新这个静态变量,我使用了一个不可变对象 ProgressStatus,创建一个新实例,然后换出引用。

var status = new ProgressStatus (50, "Working on it"); //plus many more fields in constructor

lock (_statusLocker) _status = status; // Very brief lock

这是阅读器代码

public GetProgressStatus () {

     var status = new ProgressStatus (_status.ID, _status.Description); 
     return status }

如果我不应用锁,可能发生的最坏情况是什么?

【问题讨论】:

  • 向我们展示阅读器代码也很重要。顺便说一句,object 的不变性并不能解决所有问题,分配引用的事实也不是原子的。如果 reference 在操作中间被另一个线程更改,Console.WriteLine(status.Progress + ", " + status.Text) 可能仍然 产生类似"50, Complete" 的东西。我想你必须决定这种不一致是否可以接受。
  • 好点。当我编写阅读器代码时,我也意识到了这一点 - 哈哈

标签: c# multithreading


【解决方案1】:

其他线程可能看不到新值。

确实,除非他们也被锁定,否则他们仍然可能看不到新值。

即使引用会自动更新(即它永远不会是新旧值的混合值),但这并没有说明更改何时对其他线程可见 - 或者其他线程何时会麻烦检查。 (例如,一个线程可能已将值缓存在寄存器中,并且没有任何迹象表明该线程需要检查主内存,它可能不会这样做。)

有可能通过将变量声明为 volatile 来避免这种情况 - 但老实说,我已经不再相信我完全理解 volatile 的含义。

在处理共享可变数据(读取写入)时始终如一地使用锁会使这个问题由于锁定的语义而消失。以无锁且保证正确的方式执行此操作需要对正在发生的事情有更深入的了解。 (注意这里是可变的变量,即使它引用的对象不是。)

【讨论】:

  • 关于易失性的事实如此。花了无数个小时试图获得 100% 准确的理解,但我仍然不确定我是否明白。
  • 几天前我查看了volatile (simple-talk.com/community/blogs/simonc/archive/2010/11/24/…),但我找不到任何不矛盾或不具误导性的文档。特别是,volatile. 前缀上的 CLR 文档似乎与 volatile 上的 C# 规范完全不同。在某个时候从 Microsoft CLR 人员那里获得“官方”观点会很好......
  • @thecoop:是的。我曾经认为对 volatile 变量的写入会立即进入主存,而读取总是来自主存,有效。我不认为这很简单,而且我很困惑:(最好留给专家,IMO。
  • 在我的代码中,我实际上是在尝试从 Dictionary 对象中读取数据。因此,我的更新代码可能会在读取 Dictionary 对象时输出(通过字典对象中的一些内部例程),我想可能会产生意想不到的结果
  • @Mark 909:如果它是一个普通的Dictionary<TKey, TValue>,那么这无论如何都不是线程安全的。
【解决方案2】:

如果您的程序依赖此值以可预测的方式更新,您可能会遇到竞争条件。

这是一个很好的“无锁”功能:

static void LockFreeUpdate<T>(ref T field, Func<T, T> updateFunction)
    where T : class
{
    var spinWait = new SpinWait();
    while (true)
    {
        T snapshot1 = field;
        T calc = updateFunction(snapshot1);
        T snapshot2 = Interlocked.CompareExchange(ref field, calc, snapshot1);
        if (snapshot1 == snapshot2) return;
        spinWait.SpinOnce();
    }
}

我不敢相信,我是在网上找到的

http://www.albahari.com/threading/part5.aspx#_SpinLock_and_SpinWait

【讨论】:

    【解决方案3】:

    在这种情况下您不需要锁,因为更改引用是原子操作。

    【讨论】:

    • 我不明白为什么这条评论被标记了,谁能解释一下?在我看来是合理的
    • @Mark 909:因为它不正确,正如我在评论中所解释的那样。仅仅因为更新是原子的并不意味着不需要锁(或其他一些内存屏障)。
    • 更具体地说,如果 _status 未标记为 volatile,其他线程可能不会立即看到值的变化。当我写下我的答案时,我已经假设它已经不稳定了,这不是我做过的最聪明的假设。无论如何,根据 Jon Skeet 的回答,似乎 volatile 甚至可能无法完成它所记录的内容。
    猜你喜欢
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多