18
public void MyTest()
{
  bool eventFinished = false;

  myEventRaiser.OnEvent += delegate { doStuff(); eventFinished = true; };
  myEventRaiser.RaiseEventInSeperateThread()

  while(!eventFinished) Thread.Sleep(1);

  Assert.That(stuff);
}

为什么 eventFinished 不能是 volatile 的,这有关系吗?

在我看来,在这种情况下,编译器或运行时可能会变得聪明,并且在 while 循环中“知道” eventFinished 只能为假。尤其是当您考虑将提升变量生成为类的成员并将委托生成为同一类的方法的方式时,从而剥夺了 eventFinished 曾经是局部变量这一事实的优化。

4

4 回答 4

12

存在一个线程原语,ManualResetEvent可以精确地执行此任务 - 您不想使用布尔标志。

像这样的东西应该可以完成这项工作:

public void MyTest()
{
    var doneEvent = new ManualResetEvent(false);

    myEventRaiser.OnEvent += delegate { doStuff(); doneEvent.Set(); };
    myEventRaiser.RaiseEventInSeparateThread();
    doneEvent.WaitOne();

    Assert.That(stuff);
}

关于缺乏对局部变量关键字的支持,我认为在 C# 中这在理论上是不可能volatile的,我不认为有任何理由。最有可能的是,它不被支持仅仅是因为在 C# 2.0 之前没有使用此类功能。现在,随着匿名方法和 lambda 函数的存在,这种支持可能会变得有用。如果我在这里遗漏了什么,请有人澄清一下。

于 2009-06-23T12:11:10.037 回答
10

大多数情况下,局部变量是特定于线程的,因此与之相关的问题volatile完全没有必要。

当它像您的示例一样,它是一个“捕获的”变量时,这种情况会发生变化 - 当它被默默地实现为编译器生成的类上的一个字段时。所以理论上它可能是不稳定的,但在大多数情况下,它不值得额外的复杂性。

特别是,像Monitor(aka lock) with Pulseetc 这样的东西也可以做到这一点,任何数量的其他线程结构也可以。

线程很棘手,主动循环很少是管理它的最佳方式......


重新编辑......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");
    }
}
于 2009-06-23T12:21:51.900 回答
8

如果你想让本地 var 表现得像 Volatile,你也可以使用 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);
}
于 2015-11-25T12:46:01.997 回答
2

如果引发的事件直到进程退出该局部变量的范围后才完成,会发生什么?该变量将被释放,您的线程将失败。

明智的做法是附加一个委托函数,向父线程指示子线程已完成。

于 2009-06-23T12:16:07.170 回答