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曾经是局部变量这一事实的优化。
答案 0 :(得分:12)
存在一个线程原语ManualResetEvent
来完成这项任务 - 你不想使用布尔标志。
这样的事情应该可以胜任:
public void MyTest()
{
var doneEvent = new ManualResetEvent(false);
myEventRaiser.OnEvent += delegate { doStuff(); doneEvent.Set(); };
myEventRaiser.RaiseEventInSeparateThread();
doneEvent.WaitOne();
Assert.That(stuff);
}
由于缺乏对局部变量的volatile
关键字的支持,我不认为理论上在C#中可能可能没有任何理由。最有可能的是,它不受支持仅仅因为在C#2.0之前没有使用这样的功能。现在,由于存在匿名方法和lambda函数,这种支持可能会变得有用。如果我在这里遗漏了什么,请有人澄清问题。
答案 1 :(得分:10)
在大多数方案中,局部变量特定于某个帖子,因此与volatile
相关的问题完全没必要。
当像在你的例子中,它是一个“捕获”变量时,当它被静默地实现为编译器生成的类上的字段时,这会发生变化。因此理论上它可以易变,但在大多数情况下,它不值得额外的复杂性。
特别是像Monitor
(又名lock
)和Pulse
之类的东西也可以这样做,就像任何其他线程构造一样。
线程很棘手,主动循环很少是管理它的最佳方式......
重新编辑... secondThread.Join()
将是显而易见的事情 - 但如果您真的想使用单独的令牌,请参阅下文。这种优势(比ManualResetEvent
之类的东西)是它不需要操作系统中的任何东西 - 它完全在CLI内部处理。
using System;
using System.Threading;
static class Program {
static void WriteLine(string message) {
Console.WriteLine(Thread.CurrentThread.Name + ": " + message);
}
static void Main() {
Thread.CurrentThread.Name = "Main";
object syncLock = new object();
Thread thread = new Thread(DoStuff);
thread.Name = "DoStuff";
lock (syncLock) {
WriteLine("starting second thread");
thread.Start(syncLock);
Monitor.Wait(syncLock);
}
WriteLine("exiting");
}
static void DoStuff(object lockHandle) {
WriteLine("entered");
for (int i = 0; i < 10; i++) {
Thread.Sleep(500);
WriteLine("working...");
}
lock (lockHandle) {
Monitor.Pulse(lockHandle);
}
WriteLine("exiting");
}
}
答案 2 :(得分:6)
如果你想让局部变量表现为易失性,你也可以使用Voltile.Write。如:
public void MyTest()
{
bool eventFinished = false;
myEventRaiser.OnEvent += delegate { doStuff(); Volatile.Write(ref eventFinished, true); };
myEventRaiser.RaiseEventInSeperateThread()
while(!Volatile.Read(eventFinished)) Thread.Sleep(1);
Assert.That(stuff);
}
答案 3 :(得分:1)
如果在进程退出该局部变量的范围之后直到引发的事件没有完成,会发生什么?该变量将被释放,您的线程将失败。
明智的做法是附加一个委托函数,该函数向父线程指示子线程已经完成。
答案 4 :(得分:0)
我目前正在学习所有这些内容,我自己偶然发现了一个问题:如何可靠地从另一个线程终止正在运行的循环? 我建议的解决方案是将控制变量包装在自定义结构中,以允许使用volatile关键字:
public struct VolatileBool
{
private volatile bool _value;
public VolatileBool(bool value) => _value = value;
public bool Value { get => _value; set => _value = value; }
}
此后,您可以将bool变量的声明更改为 VolatileBool 类型,然后利用该结构的volatile Value 属性。
VolatileBool eventFinished = new VolatileBool(false);
//...
while(!eventFinished.Value) Thread.Sleep(1);
最后是另一个线程:
eventFinished.Value = true;