While 循环计时如何在带有多个线程的 C# 中工作?
Posted
技术标签:
【中文标题】While 循环计时如何在带有多个线程的 C# 中工作?【英文标题】:How does While Loop timing work in C# w/ multiple threads? 【发布时间】:2021-10-10 17:13:28 【问题描述】:我有一个可以从多个线程更新的 isRunning 布尔变量。只要 isRunning 为真,我就有一个 while 循环运行。在循环开始时,它立即再次检查布尔值是否为真。从概念上讲,我可以看到 while 循环将 isRunning 视为 true 的情况,然后另一个线程在 if 语句被处理之前更新该值。在实践中,我想象 isRunning 的时间跨度将有亚毫秒的变化,以便永远命中 if 语句。对我来说,这感觉就像是臭代码。
while(isRunning)
//isRunning gets updated by another thread??
if(!isRunning)
//Will this ever get hit???
if 块可以被触发吗?在执行 while 检查之后紧跟 if 检查之间有什么样的时间跨度?我将如何进行实验?
【问题讨论】:
"if 块是否会被触发?" - 绝对地。while
条件仅在循环开始时检查。如果另一个线程可以随时更新布尔值,那么循环以 isRunning
为 true 并且 isRunning
在 if 条件之前变为 false 肯定有可能。
您可能希望使用 ManualResetEvent 来达到您的目的
您的“实践中”假设非常危险。
"在执行 while 检查和紧跟 if 检查之间有什么样的时间跨度?"您遗漏了很多代码,尤其是 while 循环的主体和在其他线程中运行的代码。所以很难说。然而,所有需要发生的只是一个简单的上下文切换,这最终是你无法控制的。简而言之 - 有可能。
优化可能意味着isRunning
存储在CPU 寄存器中,因此将永远在另一个线程上显示为false
(无限循环)。这段代码完全不是线程安全的,这种事情你需要使用Interlocked
【参考方案1】:
可以提出各种理论论证,但让我们尝试一下。我不会很小心,因为那不是重点。关键是要区分击中if
块是否是天文罕见的事件。
例如:
static void Main()
int IfBlockHitCount = 0;
for (int i = 0; i < 1000; i++)
Thread runner = new Thread(RandomlyResetRunningFlag);
IsRunning = true;
runner.Start();
while (IsRunning)
if (!IsRunning)
IfBlockHitCount++;
Console.WriteLine(IfBlockHitCount);
static volatile bool IsRunning;
static volatile int sink;
static void RandomlyResetRunningFlag()
Random r = new Random();
int spinCount = r.Next(1000, 1000000);
for (int i = 0; i < spinCount; i++)
sink = 0;
IsRunning = false;
我运行了几次,打印的数字非常多,通常不到 500(即不到一半的时间)。由此我得出结论,是的,很可能会击中 if
块。
但是为什么呢?我提出这个解释。由于执行忙等待的线程基本上做了两件事:
-
在
while
的条件下检查IsRunning
。
在if
的条件下检查IsRunning
。
真的没有其他地方可以花时间。考虑到IsRunning
首次被观察为false
的随机时间,在这两种情况下都存在相当大的机会。如果线程实际上在做任何事情,那将大大改变结果。它以何种方式改变它,取决于 在哪里线程正在做某事:
while (IsRunning)
// A
if (!IsRunning)
IfBlockHitCount++;
// B
如果在位置 A 添加更多代码,则更有可能执行 if
块。如果在位置 B 添加更多代码,则执行 if
块的可能性会降低。
换一种说法:这并不是时间窗口有多短的问题,而是时间窗口相对于该窗口之外的时间量有多大的问题。如果没有其他事情发生,即使是一个很短的窗口也可以覆盖整个时间线的大部分时间。
顺便说一句,IsRunning
必须是volatile
,否则整个问题都没有实际意义。 (提醒专家,C# volatile
不与 C++ volatile
相同,实际上适用于这种用法,尽管不一定推荐)我还做了一个 volatile @ 987654337@,为了强制那个随机持续时间的等待循环实际做某事,如果循环被优化掉会很糟糕。
【讨论】:
以上是关于While 循环计时如何在带有多个线程的 C# 中工作?的主要内容,如果未能解决你的问题,请参考以下文章
使用 sleep() 函数作为带有 while 循环的计时器不起作用?