How can I replace this semaphore with a monitor?
In previous question of mine, someone had meantioned that using Semaphores were expensive in C# compared to using a monitor. So I ask this, how can I replace the semaphore in this code with a monitor?
I need function1 to return its value after function2 (in a separate thread) has been completed. I had replaced the Semaphore.WaitOne
with a Monitor.Wait
开发者_开发百科and the Semaphore.Release
with a Monitor.PulseAll
but the PulseAll
was being triggered before the Wait
causing the program to hang. Any idea how to avoid that race condition?
Semaphore semaphore = new Semaphore(0,1);
byte b;
public byte Function1()
{
// new thread starting in Function2;
semaphore.WaitOne();
return b;
}
public void Function2()
{
// do some thing
b = 0;
semaphore.Release();
}
You can do this with a WaitHandle instead of a Semaphore. This would be the simplest alternative, and perform better than a Semaphore:
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
byte b;
public byte Function1()
{
// new thread starting in Function2;
manualResetEvent.WaitOne();
return b;
}
public void Function2()
{
// do some thing
b = 0;
manualResetEvent.Set();
}
@Reed provided an elegant solution if you need to wait for multiple threads.
You might not want to use Monitor
fro this.
As @Reed pointed out, an event would suffice and would provide the cleanest and most understandable solution that matches the requirements of your code.
The overhead of using real operating system sync primitives will most probably not matter in your case and using e.g. Monitor
would provide only diminishing returns at the cost of much higher complexity.
With that said, here is an implementation using Monitor
and signaling.
You can use a bool
flag - guarded by the lock - to indicate that you have finished and avoid waiting in that case. (A)
If you really start a new thread within Function2()
where the comments indicate and use lock()
around both WaitOne()
and Release()
, you do not need the flag at all. (B)
A, using a flag:
class Program
{
static object syncRoot = new object();
//lock implies a membar, no need for volatile here.
static bool finished = false;
static byte b;
public static byte Function1()
{
lock (syncRoot)
{
//Wait only if F2 has not finished yet.
if (!finished)
{
Monitor.Wait(syncRoot);
}
}
return b;
}
static public void Function2()
{
// do some thing
b = 1;
lock (syncRoot)
{
finished = true;
Monitor.Pulse(syncRoot);
}
}
static void Main(string[] args)
{
new Thread(Function2).Start();
Console.WriteLine(Function1());
}
}
B, starting a thread from Function1
:
class Program
{
static object syncRoot = new object();
static byte b;
public static byte Function1()
{
lock (syncRoot)
{
// new thread starting in Function2;
new Thread(Function2).Start();
Monitor.Wait(syncRoot);
}
return b;
}
static public void Function2()
{
// do some thing
b = 1;
//We need to take the lock here as well
lock (syncRoot)
{
Monitor.Pulse(syncRoot);
}
}
static void Main(string[] args)
{
Console.WriteLine(Function1());
}
}
精彩评论