类成员枚举线程是否安全?

时间:2016-02-01 19:02:22

标签: c# multithreading

以下面的例子为例

{{1}}

如果MyClass中的方法在不同的线程上运行并读取/更新了_sharedEnumVal,我是否正确地说需要一个锁或其他机制来保持变量线程像其他原语一样安全或者枚举是否特殊?

由于

2 个答案:

答案 0 :(得分:7)

线程安全是一个棘手的主题。枚举的更新始终是原子的。因此,即使数千个线程尝试同时更新相同的枚举,您也永远不会得到无效的,半更新的枚举值。值本身始终有效。但即使你更新枚举,也不能保证其他线程会因多个内核之间的缓存不相干而读取“最新”值。为了确保所有内核都是同步的,您需要一个内存屏障。

但即使这不是线程安全的保证,因为数据竞争仍然可能发生。假设你在班上的某个地方有这个逻辑:

 public void DoSomething()
 {
    if (_sharedEnumVal == MyEnum.First) {
       DoPrettyThings();
    } else {
       DoUglyThings();
    }
 }

 public void UpdateValue(MyEnum newValue)
 {
     _sharedEnumVal = newValue;
 }

你有两个不同的主题:

 static MyClass threadSafeClass = new MyClass();

 void ThreadOne()
 {
    while (true) 
    {
        threadSafeClass.UpdateValue(MyEnum.Second);
        DoSomething();
    }
 }

 void ThreadTwo()
 {
    while (true)
    {
       threadSafeClass.UpdateValue(MyEnum.First);
       DoSomething();
    }
 }

这里,尽管对枚举的更新是原子的,但是两个线程将“竞争”以改变并使用枚举值来实现它们自己的目的,并且当调用DoSomething时,无法保证枚举将具有什么值。你会得到完全意想不到的结果。 ThreadTwo可能会导致漂亮的东西,而ThreadOne会导致丑陋的事情发生,这与预期完全相反。

在这种情况下,您仍然需要锁定以确保类行为的线程安全。

答案 1 :(得分:1)

我无法理解,为什么这个话题被低估了:)。 有一些好点和一些不好的想法,有些甚至在这里赞成! 所以,让我们对这些位进行排序。

这里的问题实际上是关于原子性的。 如果操作是原子操作,那么它本身就是线程安全的,没有锁定某些操作,如读/写和其他操作,这要归功于给定类型的Interlocked类。

现在,。Net正在声明,int读/写是原子的。对于适合32位的所有类型,64位类型都不是原子的!对象引用的读/写也是原子的。

有些操作是原子操作,有些操作不是,如增量,除非你调用Interlocked.Increment。

为什么我要谈论int?嗯,默认情况下,enum的类型为int,32bit,除非另有明确说明。

这意味着,读/写是原子=>线程安全的。

顺便说一句,保持裸体属性通常是一个坏主意,我宁愿在属性后面使用变量并使用变量,因为必须使用Interlocked方法。

有许多有用的方法,其中原子性足够保证无需锁定即可使用。例如后台线程状态。或者允许后台工作人员工作的属性,直到将其更改为某个预期值,为后台工作人员提供停止信息等。

此外,Interlocked类正在为共享迭代变量扩展这些场景等等。

正如Chris Hannon所指出的那样,简单的读/写可能导致过时,因为数据不会被更新,除非特定的读/写操作将由内存屏障修饰或使用Interlocked操作,Interlocked.Add for读取,interlocked.CompareExchange用于写入,其中缓存将被更新。 感谢Chris,我错过了!

相关问题