为什么局部变量不能在C#中出现波动?

Pat*_*nga 17 c# multithreading volatile

public void MyTest()
{
  bool eventFinished = false;

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

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

  Assert.That(stuff);
}
Run Code Online (Sandbox Code Playgroud)

为什么eventFinished不能变得不稳定并且重要吗?

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

Nol*_*rin 12

存在一个线程原语,ManualResetEvent准确地执行此任务 - 您不希望使用布尔标志.

这样的事情应该做的工作:

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

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

    Assert.That(stuff);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您的线程和锁定如此复杂,性能敏感且处理器排序敏感,您需要将其标记为易失性,那么您不应该依赖编译器对局部变量进行各种疯狂的重写.课程给你.您应该明确地编写代码,以使其尽可能清晰和准确,以便将代码的确切语义传达给需要理解它的维护程序员.句法糖的意义在于隐藏机制; 你想暴露机制! (17认同)
  • 以这种方式看待它:语言不能保证将已提升的本地实现为字段.只要语言能够在运行时生成维护闭包和变量语义的代码,它们就可以实现为数组,属性或其他任何元素.我们是否要在语言中添加一个功能,该功能对允许编译器编写器生成代码的方式有很大的限制?不,特别是因为该功能一开始就是个坏主意.作为场地的悬挂本地人已经是一个漏洞的抽象; 我们不想让情况变得更糟. (7认同)
  • 埃里克什么时候需要他?;-p (2认同)
  • 好吧,正如你所指出的那样,本地本身不能被两个不同的线程访问,但是闭包*创建的字段可以*.将局部变量标记为volatile可以指示闭包生成的字段将标记为相同. (2认同)

Mar*_*ell 10

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

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

特别是,像Monitor(aka 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");
    }
}
Run Code Online (Sandbox Code Playgroud)


Fre*_*ieb 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);
}
Run Code Online (Sandbox Code Playgroud)