Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why can't a local variable be volatile in C#?

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.

like image 468
Patrick Huizinga Avatar asked Jun 23 '09 12:06

Patrick Huizinga


People also ask

Can local variable be volatile?

A local variable cannot be declared as volatile because a local variable is always private to the thread which is never shared with other threads.

Does C have volatile?

Volatile is used in C programming when we need to go and read the value stored by the pointer at the address pointed by the pointer. If you need to change anything in your code that is out of compiler reach you can use this volatile keyword before the variable for which you want to change the value.

What does the volatile variable in C indicate?

C's volatile keyword is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time--without any action being taken by the code the compiler finds nearby.

Can we use static and volatile together in C?

Even if the static variables are shared variables, but in different thread there can be different values for a static variable in the local cache of a thread. To make it consistent for all threads, just declare it as static volatile . So each time it will fetch from main memory.


2 Answers

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:

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

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

    Assert.That(stuff);
}

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.

like image 113
Noldorin Avatar answered Oct 14 '22 23:10

Noldorin


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 (aka lock) with Pulse 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 like ManualResetEvent) is that it doesn't require anything from the OS - it is handled purely inside the 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");
    }
}
like image 34
Marc Gravell Avatar answered Oct 14 '22 22:10

Marc Gravell