开发者

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());
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜