如何用监视器替换该信号量?

发布于 2024-08-27 16:52:30 字数 594 浏览 7 评论 0原文

在我之前的问题中,有人表示与使用监视器相比,在 C# 中使用信号量的成本更高。所以我问,如何用监视器替换这段代码中的信号量?

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ら栖息 2024-09-03 16:52:30

您可以使用 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();
}

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();
}
魂ガ小子 2024-09-03 16:52:30

@Reed 提供了一个 优雅的解决方案 如果需要等待多个线程。

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

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

您可以使用由锁保护的 bool 标志来指示您已完成并避免在这种情况下等待。 (一)
如果您确实在 Function2() 中启动一个新线程,其中注释指出并在 两者 周围使用 lock() WaitOne()< /code> 和 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());
    }
}

@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());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文