Let's add a new folder called Threading
to the FileStorage.Portable
project. Inside this folder, we are going to add a new file called AsyncSemaphore.cs
and implement the first part as follows:
public class AsyncSemaphore { private readonly static Task s_completed = Task.FromResult(true); private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>(); private int m_currentCount; public AsyncSemaphore(int initialCount) { if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount"); m_currentCount = initialCount; } public Task WaitAsync() { lock (m_waiters) { if (m_currentCount > 0) { --m_currentCount; return s_completed; } else { var waiter = new TaskCompletionSource<bool>(); m_waiters.Enqueue(waiter); return waiter.Task; } } } }
The AsyncSemaphore
keeps a count (the m_count
property), which is the number of open slots it has available to satisfy waiters.
The Task
returned from the WaitAsync
function (the static s_completed
property) will enter the completed state when the AsyncSemaphore
has given it an available slot. That same Task
will enter the Canceled
state if the CancellationToken
is signaled before the wait is satisfied; in that case, the AsyncSemaphore
does not lose a slot.
18.191.174.168