以下是线程安全的吗?

时间:2016-06-28 15:19:54

标签: c# thread-safety

我有以下代码:

public class Info
{
    public int Data { get; set; }
}

public class Updater
{
    private static readonly object lockObject = new object();
    private Info myInfo= new Info();

    public Info MyInfo
    {
        get
        {
            lock (lockObject)
            {
                return myInfo;
            }
        }
    }

    public void UpdateInfo()
    {
        lock (lockObject)
        {
            myInfo.Data = ReadFromExternalDevice();
        }
    }        
}

我有一个Updater实例,可以从两个不同的线程访问。

Updater updater = new Updater();

线程#1定期调用UpdateInfo()

updater.UpdateInfo();

线程#2定期从Data属性的Info属性中读取。

int latestData = updater.MyInfo.Data;

以上代码是否接近线程安全?

2 个答案:

答案 0 :(得分:2)

想象一下

  • 第一个帖子获得MyInfo离开锁定return myInfo;后)
  • 第一个帖子开始阅读MyInfo属性
  • 第二个帖子开始写入MyInfo
  • 第一个帖子继续阅读MyInfo(部分更改!)属性

在这种情况下,代码线程安全。即使MyInfo只有一个int字段,也不是线程安全

MyInfo info = Updater.Info;

Console.Write(info.Data);

// Here 2nd thread changes the info
Console.Write(info.Data);

典型实施使用ReaderWriterLockSlim

https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110).aspx

  • 读取第一个帖子EnterReadLock()时将(克隆MyInfo读入本地变量(或使用MyInfo执行所有必需的过程),然后{ {1}}
  • 编写第二个帖子ExitReadLock()时写入,然后EnterWriteLock()

答案 1 :(得分:1)

如果您只关心读取 Data属性,那么这个实现就是......让我们说......线程足够安全。

对于32位值的读写操作在C#中是 atomic ,因此updater.MyInfo.Data中任何时候都没有奇怪的中间值。如果您有更新的更复杂的属性,则不再适用。线程2可能只是部分更新了这些属性,而线程1仍然在相同的MyInfo上阅读。

如果您尝试执行类似

的操作
if (updater.MyInfo.Data == 5)
    updater.MyInfo.Data = 7;

在不同的线程中,不再保证按预期运行。由于另一个线程可能在检查和分配之间更改了Data的值。

您可能需要查看Interlocked.Exchange以获取更新变量的线程安全方法。