【问题标题】:What's the proper way to wait on a Semaphore?等待信号量的正确方法是什么?
【发布时间】:2026-02-23 08:00:01
【问题描述】:

我认为下面的代码会让所有 10 个线程运行,一次运行两个,然后在调用 Release() 10 次后打印“done”。但事实并非如此:

        int count = 0;

        Semaphore s = new Semaphore(2, 2);
        for (int x = 0; x < 10; x++)
        {
            Thread t = new Thread(new ThreadStart(delegate()
            {
                s.WaitOne();
                Thread.Sleep(1000);
                Interlocked.Increment(ref count);                       
                s.Release();
            }));
            t.Start(x);
        }

        WaitHandle.WaitAll(new WaitHandle[] { s });
        Console.WriteLine("done: {0}", count);

输出:

done: 6

如果实现我正在寻找的功能的唯一方法是将EventWaitHandle 传递给每个线程,然后在这些EventWaitHandles 的数组上执行WaitAll(),那么执行@ 的含义是什么987654328@ 在只有一个信号量的数组上?也就是说,等待线程什么时候解除阻塞?

【问题讨论】:

  • 您很可能会看到本地化的竞争条件; Console.WriteLine 有它自己的锁,输出内容的顺序不能保证是你调用它的顺序。您可能想要编写刻度值或使用更好地处理这种情况的日志框架。输出没有显示两个线程没有同时运行两个线程。
  • 仅供参考,您可能需要考虑使用Interlocked.Increment 而不是volatile*.com/questions/154551/…
  • casperOne,我不确定这是否正确,因为即使我将最后一行替换为 System.Diagnostics.Debug.WriteLine("done"); 也会遇到同样的问题;
  • 我不太确定这个例子的意义是什么——反正你要启动 10 个线程。如果你想一次运行两个线程,为什么不只打开两个线程并平均分配工作。
  • 如果您想等待所有线程完成,请保留Thread 对象并依次调用每个对象上的Join

标签: c# .net multithreading semaphore waithandle


【解决方案1】:

WaitHandle.WaitAll 只是等到所有处理程序都处于信号状态。

因此,当您在 one WaitHandle 上调用 WaitHandle.WaitAll 时,它的工作方式与您调用 s.WaitOne() 时相同

例如,您可以使用以下代码等待所有启动的线程,但允许两个线程并行运行:

int count = 0;

Semaphore s = new Semaphore(2, 2);
AutoResetEvent[] waitHandles = new AutoResetEvent[10];
for (int x = 0; x < 10; x++)
    waitHandles[x] = new AutoResetEvent(false);

for (int x = 0; x < 10; x++)
{
    Thread t = new Thread(threadNumber =>
        {
            s.WaitOne();
            Thread.Sleep(1000);
            Interlocked.Increment(ref count);
            waitHandles[(int)threadNumber].Set();
            s.Release();
        });
    t.Start(x);
}

WaitHandle.WaitAll(waitHandles);
Console.WriteLine("done: {0}", count);

【讨论】:

    【解决方案2】:

    WaitHandle.WaitAll(new WaitHandle[] { s }); 就像 s.WaitOne(); 一样等待。它一有机会就进入。您似乎希望此调用等待所有其他信号量操作,但操作系统无法区分。该命令很可能是第一个被授予访问信号量的命令。

    我认为您需要的是 Barrier 类。它是为 fork-join 风格的并行性而设计的。

    【讨论】:

    • @caesay Barrier 课程在网络上肯定有很多可用的教程。它易于使用。
    最近更新 更多