Perhaps it\'s too late at night, but I can\'t think of a nice way to do this.
I\'ve started a bunch of asynchronous downloads, and I want to wait until they all comp
Based on the suggestions here, this is what I came up with. In addition to waiting until the count is 0, it will sleep if you spawn too many threads (count > max). Warning: This is not fully tested.
public class ThreadCounter
{
#region Variables
private int currentCount, maxCount;
private ManualResetEvent eqZeroEvent;
private object instanceLock = new object();
#endregion
#region Properties
public int CurrentCount
{
get
{
return currentCount;
}
set
{
lock (instanceLock)
{
currentCount = value;
AdjustZeroEvent();
AdjustMaxEvent();
}
}
}
public int MaxCount
{
get
{
return maxCount;
}
set
{
lock (instanceLock)
{
maxCount = value;
AdjustMaxEvent();
}
}
}
#endregion
#region Constructors
public ThreadCounter() : this(0) { }
public ThreadCounter(int initialCount) : this(initialCount, int.MaxValue) { }
public ThreadCounter(int initialCount, int maximumCount)
{
currentCount = initialCount;
maxCount = maximumCount;
eqZeroEvent = currentCount == 0 ? new ManualResetEvent(true) : new ManualResetEvent(false);
}
#endregion
#region Public Methods
public void Increment()
{
++CurrentCount;
}
public void Decrement()
{
--CurrentCount;
}
public void WaitUntilZero()
{
eqZeroEvent.WaitOne();
}
#endregion
#region Private Methods
private void AdjustZeroEvent()
{
if (currentCount == 0) eqZeroEvent.Set();
else eqZeroEvent.Reset();
}
private void AdjustMaxEvent()
{
if (currentCount <= maxCount) Monitor.Pulse(instanceLock);
else do { Monitor.Wait(instanceLock); } while (currentCount > maxCount);
}
#endregion
}
Well... you can snatch all the semaphore counters on the main thread back in order to blocks when count is 0, rather than non-zero.
REVISED: Here I assumed 3 things:
So here's my solution, revised:
Initializes the Semaphore with a large enough counter so you never hit the maximum (it could be simply 100 or just 10 depending on your situation):
var maxDownloads = 1000;
_semaphore = new Semaphore(0, maxDownloads);
Then on each downloads, begins with WaitOne() before starting the download so that in the event of program exiting, no downloads can start.
if (_semaphore.WaitOne())
/* proceeds with downloads */
else
/* we're terminating */
Then on download completion, release one counter (if we had acquired one):
finally { _semaphore.Release(1); }
And then on the "Exit" event, consume up all the counters on the Semaphore:
for (var i = 0; i < maxDownloads; i++)
_semaphore.WaitOne();
// all downloads are finished by this point.
...