为什么 C# 中的局部变量不能是 volatile?
public void MyTest()
{
bool eventFinished = false;
myEventRaiser.OnEvent += delegate { doStuff(); eventFinished = true; };
myEventRaiser.RaiseEventInSeperateThread()
while(!eventFinished) Thread.Sleep(1);
Assert.That(stuff);
}
为什么 eventFinished 不能是易失性的,这很重要吗?
在我看来,在这种情况下,编译器或运行时可能会为了自己的利益而变得聪明,并且在 while 循环中“知道” eventFinished 只能为 false。 特别是当您考虑将提升变量生成为类的成员并将委托生成为同一类的方法时,从而剥夺了对 eventFinished 曾经是局部变量这一事实的优化。
public void MyTest()
{
bool eventFinished = false;
myEventRaiser.OnEvent += delegate { doStuff(); eventFinished = true; };
myEventRaiser.RaiseEventInSeperateThread()
while(!eventFinished) Thread.Sleep(1);
Assert.That(stuff);
}
Why can't eventFinished be volatile and does it matter?
It would seem to me that in this case the compiler or runtime could become to smart for its own good and 'know' in the while loop that eventFinished can only be false. Especially when you consider the way a lifted variable gets generated as a member of a class and the delegate as a method of that same class and thereby depriving optimizations of the fact that eventFinished was once a local variable.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果你想让本地变量表现得像 Volatile,你也可以使用 Voltile.Write。 如:
You could also use Voltile.Write if you want to make the local var behave as Volatile. As in:
如果引发的事件直到进程退出该局部变量的范围后才完成,会发生什么? 该变量将被释放,并且您的线程将失败。
明智的方法是附加一个委托函数,向父线程指示子线程已完成。
What would happen if the Event raised didn't complete until after the process had exited the scope of that local variable? The variable would have been released and your thread would fail.
The sensible approach is to attach a delegate function that indicates to the parent thread that the sub-thread has completed.
存在一个线程原语
ManualResetEvent
要精确地完成此任务 - 您不想使用布尔标志。像这样的事情应该可以完成这项工作:
关于局部变量上缺乏对
volatile
关键字的支持,我不认为有任何理由可以解释为什么这在理论上是不可能的 em> 在 C# 中。 最有可能的是,它不受支持只是因为在 C# 2.0 之前没有使用此功能。 现在,随着匿名方法和 lambda 函数的存在,这种支持可能会变得有用。 如果我在这里遗漏了什么,请有人澄清。There exists a threading primitive,
ManualResetEvent
to do precisely this task - you don't want to be using a boolean flag.Something like this should do the job:
Regarding the lack of support for the
volatile
keyword on local variables, I don't believe there is any reason why this might not in theory be possible in C#. Most likely, it is not supported simply because there was no use for such a feature prior to C# 2.0. Now, with the existence of anonymous methods and lambda functions, such support could potentially become useful. Someone please clarify matters if I'm missing something here.在大多数场景中,局部变量是特定于线程的,因此与
易失性
相关的问题是完全没有必要的。当它是一个“捕获的”变量时(就像在您的示例中一样),当它被默默地实现为编译器生成的类上的字段时,这种情况会发生变化。 因此,从理论上讲,它可能会不稳定,但在大多数情况下,不值得付出额外的复杂性。
特别是,带有
Pulse
等的Monitor
(又名lock
)等也可以做到这一点,任何数量的其他线程构造也可以做到这一点。线程很棘手,并且主动循环很少是管理它的最佳方法...
重新编辑...
secondThread.Join()
将是显而易见的事情 - 但如果你真的想使用一个单独的令牌,见下文。 这样做的优点(相对于ManualResetEvent
之类的东西)是它不需要操作系统提供任何内容 - 它纯粹在 CLI 内处理。In most scenarios, local variables are specific to a thread, so the issues associated with
volatile
are completely unnecessary.This changes when, like in your example, it is a "captured" variable - when it is silently implemented as a field on a compiler-generated class. So in theory it could be volatile, but in most cases it wouldn't be worth the extra complexity.
In particular, something like a
Monitor
(akalock
) withPulse
etc could do this just as well, as could any number of other threading constructs.Threading is tricky, and an active loop is rarely the best way to manage it...
Re the edit...
secondThread.Join()
would be the obvious thing - but if you really want to use a separate token, see below. The advantage of this (over things likeManualResetEvent
) is that it doesn't require anything from the OS - it is handled purely inside the CLI.