mik*_*ike 5 c# multithreading atomicity
以下代码是线程安全的吗?
public object DemoObject {get;set;}
public void DemoMethod()
{
if (DemoObject is IDemoInterface demo)
{
demo.DoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
如果其他线程在处理时修改DemoObject(例如设置为空)DemoMethod,是否保证在if块内局部变量demo总是被正确分配(给类型的实例IDemoInterface)?
这里的 is 构造是原子的,就像互锁一样。然而,这段代码的行为几乎 100% 是不确定的。除非目标是创建不可预测和不确定的行为,否则这将是一个错误。
\n此代码的有效使用示例:在模拟某些不确定性事件的可能性的游戏中,例如“来自矩阵的尼奥在半空中捕获子弹”,此方法可能比简单地使用伪随机数生成器更具不确定性。
\n在任何需要确定性/可预测行为的场景中,此代码都是一个错误。
\n解释:
\nif(DemoObject 是 IDemoInterface 演示)
\n以伪原子方式评估和分配。
\n此后,在 if 语句中:\n即使 DemoObject 被另一个线程设置为 null,demo 的值也已被分配,并且 DoSomething() 操作将在已分配的实例上执行。
\n回答您的评论问题:
\n\n\n为什么会有一场比赛?
\n
竞争条件是此代码中设计的。在下面的示例代码中:
\n请参阅:什么是竞争条件?
\n\n\n为什么我无法预测 DoSomething() 是否会执行?
\n
DoSomething() 每次都会执行
\nif (DemoObject is IDemoInterface demo)\nRun Code Online (Sandbox Code Playgroud)\n评估为真。每次 DemoObject 为 null 或 NOT IDemoInterface 时,它都不会执行。\n您无法预测它何时执行。您只能预测,每当执行 DoSomething() 的线程设法获取对 DemoObject 的非 null 实例的引用时,它将执行。或者换句话说,当运行 DemoMethod() 的线程设法赢得竞争条件时:
\nA) 运行 DemoMethod_Assign() 的线程赢得竞争条件后
\nB) 在运行 DemoMethod_Null() 的线程赢得竞争条件之前
\n警告:根据我的理解(请其他人澄清这一点),DemoObject 可能在不同线程中同时为 null 和非 null。
\nDemoObject可以从高速缓存读取或者可以从主存储器读取。我们不能让它变得易失,因为它是一个对象引用。因此,DemoObject 的状态对于一个线程可能同时为 Null,而对于另一线程则不为 null。这意味着它的值是不确定的。在Schr\xc3\xb6dinger\的猫中,猫同时是死的和活的。我们这里的情况也很相似。
\n这段代码中没有关于 DemoObject 的锁或内存屏障。然而,线程上下文切换会强制执行相当于内存屏障的操作。因此,上下文切换后恢复的任何线程都将拥有从主内存检索到的 DemoObject 值的准确副本。然而,不同的线程可能已经改变了 DemoObject 的值,但这个改变的值可能尚未刷新到主内存。这就带来了一个问题:哪个是实际的准确值?从主存中获取的值或尚未刷新到主存的值。
\n注意:其他人请澄清这个警告,因为我可能错过了一些东西。
\n下面是一些代码,用于验证除警告之外的所有内容。在具有 64 个逻辑核心的计算机上运行此控制台应用程序测试。永远不会抛出空引用异常。
\ninternal class Program\n{\n private static ManualResetEvent BenchWaitHandle = new ManualResetEvent(false);\n\n private class DemoClass : IDemoInterface\n {\n public void DoSomething()\n {\n Interlocked.Increment(ref Program.DidSomethingCount);\n }\n }\n private interface IDemoInterface\n {\n void DoSomething();\n }\n private static object DemoObject { get; set; }\n public static volatile int DidSomethingCount = 0;\n\n private static void DemoMethod()\n {\n BenchWaitHandle.WaitOne();\n for (int i = 0; i < 100000000; i++)\n {\n try\n {\n if (DemoObject is IDemoInterface demo)\n {\n demo.DoSomething();\n }\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.ToString());\n }\n }\n }\n private static bool m_IsRunning = false;\n private static object RunningLock = new object();\n private static bool IsRunning \n { \n get { lock (RunningLock) { return m_IsRunning; } } \n set { lock(RunningLock) { m_IsRunning = value; } }\n }\n private static void DemoMethod_Assign()\n {\n BenchWaitHandle.WaitOne();\n while (IsRunning)\n {\n DemoObject = new DemoClass();\n }\n }\n private static void DemoMethod_Null()\n {\n BenchWaitHandle.WaitOne();\n while (IsRunning)\n {\n DemoObject = null;\n }\n }\n static void Main(string[] args)\n {\n List<Thread> threadsListDoWork = new List<Thread>();\n List<Thread> threadsList = new List<Thread>();\n BenchWaitHandle.Reset();\n for (int I =0; I < 16; I++)\n {\n threadsListDoWork.Add(new Thread(new ThreadStart(DemoMethod)));\n threadsList.Add(new Thread(new ThreadStart(DemoMethod_Assign)));\n threadsList.Add(new Thread(new ThreadStart(DemoMethod_Null)));\n }\n foreach (Thread t in threadsListDoWork)\n {\n t.Start();\n }\n foreach (Thread t in threadsList)\n {\n t.Start();\n }\n IsRunning = true;\n BenchWaitHandle.Set();\n foreach (Thread t in threadsListDoWork)\n {\n t.Join();\n }\n IsRunning = false;\n foreach (Thread t in threadsList)\n {\n t.Join();\n }\n\n Console.WriteLine(@"Did Something {0} times", DidSomethingCount);\n Console.ReadLine();\n}\n//On the last run this printed\n//Did Something 112780926 times\n//Which means that DemoMethod() threads won the race condition slightly over 7% of the time.\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
82 次 |
| 最近记录: |