Mar*_*ari 25 c# thread-safety switch-statement atomicity
请考虑以下示例代码:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
忘掉片段的无用性:我的疑问是关于switch声明的行为.
假设该x字段只能有两个值:0xFF00000000L或0xFFL.上面的开关不应该属于"默认"选项.
现在想象一个线程正在执行"x"等于0xFFL的开关,因此第一个条件将不匹配.同时,另一个线程将"x"变量修改为0xFF00000000L.我们知道64位操作不是原子操作,因此变量首先将较低的dword置零,然后将较低的dword置零(反之亦然).
如果在"x"为零时(即在新的分配期间)完成开关中的第二个条件,我们是否会陷入不希望的"默认"情况?
Luk*_*keH 16
是的switch,如您的问题所示,声明本身是线程安全的.字段的值一次x加载到(隐藏的)局部变量中,并且该局部用于块.switch
不安全的是将字段初始加载x到局部变量中.64位读取不保证是原子的,因此您可能会在此时获得陈旧和/或撕裂的读取.通过使用Interlocked.Read或类似地以线程安全的方式将字段值显式读入本地,可以很容易地解决这个问题:
long y = Interlocked.Read(ref x);
switch (y)
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
Jor*_*oba 12
你实际上发布了两个问题.
它是线程安全的吗?
好吧,显然不是,另一个线程可能会在第一个线程进入交换机时更改X的值.由于没有锁定且变量不易变,因此您将根据错误的值进行切换.
你有没有达到开关的默认状态?
理论上你可能,因为更新64位不是原子操作,因此理论上你可以跳转到赋值的中间并得到x的混合结果,如你所指出的那样.这在统计上不会经常发生,但最终会发生.
但是交换机本身是线程安全的,不是线程安全的是读取和写入64位变量(在32位操作系统中).
想象一下,而不是开关(x),你有以下代码:
long myLocal = x;
switch(myLocal)
{
}
Run Code Online (Sandbox Code Playgroud)
现在,切换是通过局部变量进行的,因此,它完全是线程安全的.当然,问题在于myLocal = x 阅读及其与其他任务的冲突.
| 归档时间: |
|
| 查看次数: |
2107 次 |
| 最近记录: |