"switch"语句评估线程是否安全?



考虑以下示例代码:

class MyClass
{
    public long x;
    public void DoWork()
    {
        switch (x)
        {
            case 0xFF00000000L:
                // do whatever...
                break;
            case 0xFFL:
                // do whatever...
                break;
            default:
                //notify that something going wrong
                throw new Exception();
        }
    }
}

忘记这段代码的无用性:我怀疑的是switch语句的行为。

假设x字段只能有两个值:0xFF00000000L0xFFL。上面的开关不应该属于"default"选项。

现在想象一个线程正在执行"x"等于0xFFL的开关,因此第一个条件不匹配。同时,另一个线程将"x"变量修改为0xFF00000000L。我们知道64位操作不是原子操作,因此该变量将先将较低的双字归零,然后再将较高的双字归零(反之亦然)。

如果开关中的第二个条件将在"x"为零时完成(即在新的赋值期间),我们会陷入不希望的"默认"情况吗?

是的,switch语句本身,如您的问题所示,是线程安全的。字段x的值被加载到一次到一个(隐藏的)局部变量中,该局部变量用于switch块。

不安全的是将字段x初始加载到局部变量中。 64位读不能保证是原子性的,因此此时您可能会得到过时和/或撕裂的读。这可以很容易地通过使用Interlocked.Read或类似的方法来解决,以线程安全的方式显式地将字段值读入本地:

long y = Interlocked.Read(ref x);
switch (y)
{
    // ...
}

你实际上发布了两个问题。

是线程安全的吗?

显然不是,当第一个线程进入switch时,另一个线程可能会改变X的值。因为没有锁,而且变量不是易失的,所以你会根据错误的值进行切换。

你会碰到开关的默认状态吗?

理论上是可以的,因为更新64位不是原子操作,所以理论上你可以跳到赋值的中间,得到x的混合结果。从统计数据来看,这种情况不会经常发生,但最终会发生的。

但是开关本身是threadsafe的不是threadsafe的是读写64位变量(在32位操作系统中)。

假设你有以下代码而不是switch(x):

long myLocal = x;
switch(myLocal)
{
}

现在在一个局部变量上进行切换,因此,它是完全线程安全的。当然,问题在于myLocal = x read及其与其他赋值的冲突。

c#的switch语句不是作为一系列if条件计算的(而VB可以)。c#根据对象的值有效地构建了一个标签哈希表,并直接跳转到正确的标签,而不是依次遍历每个条件并求值。

这就是为什么c# switch语句不会随着案例数量的增加而减慢速度的原因。这也是为什么c#比VB更限制你在切换情况下可以比较的东西,例如,在VB中你可以做值的范围。

因此,你没有潜在的竞争条件,你已经说过,在比较中,值改变,第二次比较,等等,因为只执行了一次检查。至于它是否完全是线程安全的,我不这么认为。

在IL中使用反射器查看c# switch语句,您将看到发生了什么。将其与VB中的switch语句进行比较,其中包含值的范围,您将看到差异。

我已经好几年没看它了,所以情况可能略有变化…

在c#中使用if/else和switch-case有什么显著的区别吗?

正如您已经假设的那样,switch语句不是线程安全的,在某些情况下可能会失败。

此外,在实例变量上使用lock也不起作用,因为lock语句期望object导致实例变量被装箱。每次实例变量被装箱,一个新的装箱变量将被创建,使lock有效无用。

在我看来,你有几个选择来解决这个问题。

  1. 在任何引用类型的私有实例变量上使用lock (object将完成这项工作)
  2. 使用ReaderWriterLockSlim允许多个线程读取实例变量,但一次只有一个线程写入实例变量。
  3. 将实例变量的值自动存储到方法中的局部变量中(例如使用Interlocked.ReadInterlocked.Exchange),并在局部变量上执行switch。注意,这样您可能会使用switch的旧值。您必须确定这是否会在您的具体用例中导致问题。

相关内容

  • 没有找到相关文章

最新更新