why can't a local variable be volatile in C#?
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.
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");
}
}
You could also use Voltile.Write if you want to make the local var behave as Volatile. As in:
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);
}