有没有办法等待多个信号量

时间:2009-05-05 20:26:11

标签: c# multithreading

我正在尝试编写一个可以同时等待多个资源池的应用程序。每个资源池由Semaphor控制。我可以使用WaitHandle.WaitAll()传递整个信号列表吗?此实现是否存在潜在的死锁问题?

我目前的实施:

namespace XXX
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;

    public class ResourcePoolManager
    {
        private readonly IDictionary<string, Semaphore> resourcePools = new Dictionary<string, Semaphore>();

        public void AddResourcePool(string resourceName, int maxConcurrentConsumers)
        {
            this.resourcePools.Add(resourceName, new Semaphore(maxConcurrentConsumers, maxConcurrentConsumers));
        }

        public void RequestResource(string resourceName)
        {
            this.resourcePools[resourceName].WaitOne();
        }

        public void RequestMultipleResources(string[] resourceNames)
        {
            Semaphore[] resources = resourceNames.Select(s => this.resourcePools[s]).ToArray();

            WaitHandle.WaitAll(resources);
        }

        public void ReleaseResource(string resourceName)
        {
            this.resourcePools[resourceName].Release(1);
        }
    }
}

3 个答案:

答案 0 :(得分:6)

当然,你可以使用它,但它只会在所有信号量被立即触发时触发。根据应用程序其余部分的结构,可能确实存在饥饿问题。例如,如果您有两个资源,A和B,以及三个线程:

  1. 持续获取资源A,使用它一秒钟,然后释放它并循环
  2. 持续获取资源B,使用它一秒钟,然后释放它并循环
  3. 等待A和B都可用
  4. 您可以轻松地等待A和B同时可用。

    根据您的应用程序,简单地按顺序获取每个信号量可能会更好,这可以避免这种饥饿问题,但会引入传统的死锁问题。但是,如果您确定这些锁在大多数情况下都可用,那么它可能是安全的(但也可能是等待您的应用程序在实际负载下的定时炸弹......)

    给出您的示例代码,另一个选项是在信号量上创建全局排序 - 比如按名称排序 - 并始终确保按顺序获取它们。如果这样做,只需逐个锁定每个信号量,就可以执行多重锁定。

    在这种情况下,释放的顺序并不严格 - 但是如果你发布了乱序,你应该在你刚刚获得的锁之后释放所有锁'之后再获取更多锁(这是一个经验法则,应该给你死锁安全。有可能通过详细分析进一步放松。)推荐的方法是尽可能以相反的顺序发布,在这种情况下,您可以在任何时候进行进一步的采集。例如:

    1. 获取锁定A
    2. 获取锁B
    3. 获取锁定C
    4. 释放锁定C
    5. 获取锁D
    6. 发行版B(现在在发布D之前不会获得任何东西!)
    7. 发布D
    8. 获取E
    9. 发布E
    10. 发布A
    11. 只要一切都遵循这些规则,就不可能出现死锁,因为服务员的周期无法形成。

      这种方法的缺点是它可能会在等待另一个线程时通过锁定来延迟其他线程。这不会永远持续下去;在上面三个线程的例子中,我们可能有这种情况,例如:

      1. 开始时,线程2保持B.线程1保持A。
      2. A上的3号线程。
      3. (时间过去了)
      4. 主题1发布A。
      5. 线程3锁定A,阻止B。
      6. 线程1阻止A。
      7. (时间过去了)
      8. 线程2发布B.
      9. 线程3锁定B,确实有效,然后解锁。
      10. 线程1锁定A,取得进展。
      11. 正如您所看到的,即使没有真正的工作要做,也有一些停机时间,即线程1在A上被阻止。但是,通过这样做,我们大大提高了线程3取得进展的机会。

        这是否是一个很好的权衡将取决于你的应用程序 - 如果你可以明确地说多个线程永远不会进入锁定,它甚至可能不重要。但是没有一种正确的方法:)

答案 1 :(得分:2)

你的意图是什么?

  1. 您是否需要等到所有信号量都发出信号? 如果是这样,请参阅之前的帖子。

  2. 您是否需要等到任何一个信号量发出信号? 如果是这样,请改用WaitAny()。

  3. 注意: WaitAny接受一个等待句柄数组,并返回发出信号的句柄的索引。

    如果发出多个句柄信号,WaitAny将返回第一个句柄

答案 2 :(得分:0)

WaitAll的全部目的是避免仅获取您正在等待的某些对象的死锁。当且仅当您尝试使用的所有信号量都可用时,WaitAll才会成功。除非它们全部可用,否则不会锁定它们中的任何一个。