如何用显示器替换此信号量?

时间:2010-04-02 17:49:58

标签: c# multithreading monitor semaphore race-condition

在我之前的一个问题中,有人曾暗示过使用信号量与使用显示器相比,C#的价格昂贵。所以我问这个问题,如何用监视器替换此代码中的信号量?

我需要function1在完成function2(在一个单独的线程中)后返回它的值。我已将Semaphore.WaitOne替换为Monitor.WaitSemaphore.Release替换为Monitor.PulseAllPulseAll在导致该程序的Wait之前被触发挂。知道如何避免这种竞争条件吗?

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

2 个答案:

答案 0 :(得分:9)

您可以使用WaitHandle而不是信号量来完成此操作。这将是最简单的替代方案,并且比信号量表现更好:

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

答案 1 :(得分:2)

如果你需要等待多个线程,@ Reed提供了elegant solution

您可能不想使用此Monitor。 正如@Reed指出的那样,一个事件就足够了,它将提供最符合您代码要求的最清晰,最易理解的解决方案。
使用真实操作系统同步原语的开销很可能在您的情况下并不重要,例如使用Monitor只会以更高的复杂性为代价提供递减收益。

话虽如此,这是一个使用Monitor和信令的实现。

您可以使用bool标志 - 由锁保护 - 表示您已完成并避免在此情况下等待。 (A)
如果你真的在Function2()内开始了一个新帖子,其中评论显示并在{em> lock()WaitOne()周围使用Release(),则不需要总的来说。 (B)

A,使用旗帜:

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