Is there a way to wait for several semaphores

I'm trying to write an application that can wait for multiple resource pools at once. Each resource pool is managed using Semaphor . Can I use WaitHandle.WaitAll() , where I go to the entire list of semaphores? Is there a potential deadlock problem with this implementation?

My current implementation:

 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); } } } 
+4
source share
3 answers

Of course, you can use it, but it will only work if all semaphores are started at once. Depending on how the rest of your application is structured, hunger problems may occur. For example, if you have two resources: A and B and three threads:

  • Take resource A continuously, work with it for one second, then release it and start
  • Constantly take resource B, work with it for one second, then release it and start
  • Wait for both A and B to be available.

You can easily wait potentially forever for simultaneous access of both A and B.

Depending on your application, it might be better to just take each semaphore in order, which avoids this problem of starvation, but introduces traditional deadlock problems. However, if you are sure that these locks will be available most of the time, this can be safe (but it can also be a ticking time bomb waiting for your application to be under real load ...)

Given your code example, another option would be to create a global ordering by semaphores β€” for example, ordering by name β€” and always make sure to get them in that order. If you do this, you can multiblock by simply locking each semaphore one by one in ascending order.

In this case, the release order does not matter much - but if you fail, you must release all the locks after the β€œlock you just released before acquiring more” (this is the rule that should provide you with security at an impasse. , it will be possible if you continue the detailed analysis). The recommended method is to simply release the acquisitions in the reverse order, where possible, in which case you can transfer it to a further acquisition at any time. For instance:

  • Lock Capture A
  • Lock Lock B
  • C lock lock
  • Lock C Lock
  • Lock Lock D
  • Release B (don't get anything now until you release D!)
  • Issue D
  • Get E
  • Issue E
  • Issue A

As long as everything follows these rules, a dead end should not be possible, as waiter cycles cannot form.

The disadvantage of this approach is that it can delay other threads while holding a lock while waiting for another. It will not last forever; in the above example of three threads, we can have such a scenario, for example:

  • At start, thread 2 is executed. Line 1 contains A.
  • Insert 3 blocks on A.
  • (time)
  • Topic 1 Release A.
  • Thread 3 of lock A, lock B.
  • Insert 1 block on A.
  • (time)
  • Topic 2 of issue B.
  • Entering 3 lock B, works, then unlocks.
  • Threads 1 lock A, progress.

As you can see, there was some downtime when Thread 1 was blocked on A, although there was no real work. However, by doing this, we have significantly improved the chances for Thread 3 to progress.

Whether this is a good compromise will depend on your application - if you can definitively say that multiple threads never enter the lock, it may not even matter. But there is no one right way :)

+6
source

What is your intention?

  • Do you need to wait until ALL semaphores are signaled? If so, see Previous Posts.

  • Do you need to wait until one of the semaphores becomes a signal? If so, use WaitAny ().

Notes: WaitAny accepts an array of wait descriptors and returns the index of the descriptor that received the signaling.

If more than one descriptor is passed, WaitAny returns the first

+2
source

WaitAll's goal is to avoid the possibility of deadlocks for acquiring only certain objects that you expect. WaitAll will be successful if and only if all the semaphores you are trying to make are available. If they are not available, they will not block any of them.

0
source

All Articles