While 循环计时在多线程 C# 中如何工作?
How does While Loop timing work in C# w/ multiple threads?
我有一个可以从多个线程更新的 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
方块是否是天文学上罕见的事件。
例如:
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
。
真的没有其他地方可以花时间。给定一个 运行dom-ish 时间,在该时间首次观察到 IsRunning
是 false
,在这两种情况中的任何一种情况下都有很大的机会。如果线程实际上 正在做 任何事情,那将大大改变结果。它改变它的方式取决于线程在何处做某事:
while (IsRunning)
{
// A
if (!IsRunning)
IfBlockHitCount++;
// B
}
如果在位置 A 中添加更多代码,那么它更有可能执行 if
块。如果在 B 位置添加更多代码,则执行 if
块的可能性会降低。
换句话说:这并不是 window 时间有多短的问题,而是相对于 [=57] 之外的时间量而言它有多大的问题=].如果没有其他事情发生,即使是很短的 window 也可以涵盖整个时间线的大部分内容。
顺便说一下,IsRunning
必须是 volatile
,否则整个问题就没有意义了。 (作为对专家的提醒,C# volatile
与 C++ volatile
不同 并且实际上适用于这种用法,尽管不一定推荐) 我还制作了一个 volatile sink
,以强制 运行dom-duration 等待循环实际执行某些操作,如果循环被优化掉,那就太糟糕了。
我有一个可以从多个线程更新的 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
方块是否是天文学上罕见的事件。
例如:
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
。
真的没有其他地方可以花时间。给定一个 运行dom-ish 时间,在该时间首次观察到 IsRunning
是 false
,在这两种情况中的任何一种情况下都有很大的机会。如果线程实际上 正在做 任何事情,那将大大改变结果。它改变它的方式取决于线程在何处做某事:
while (IsRunning)
{
// A
if (!IsRunning)
IfBlockHitCount++;
// B
}
如果在位置 A 中添加更多代码,那么它更有可能执行 if
块。如果在 B 位置添加更多代码,则执行 if
块的可能性会降低。
换句话说:这并不是 window 时间有多短的问题,而是相对于 [=57] 之外的时间量而言它有多大的问题=].如果没有其他事情发生,即使是很短的 window 也可以涵盖整个时间线的大部分内容。
顺便说一下,IsRunning
必须是 volatile
,否则整个问题就没有意义了。 (作为对专家的提醒,C# volatile
与 C++ volatile
不同 并且实际上适用于这种用法,尽管不一定推荐) 我还制作了一个 volatile sink
,以强制 运行dom-duration 等待循环实际执行某些操作,如果循环被优化掉,那就太糟糕了。