You should not block ThreadPool
threads, this is a quick way to lead to ThreadPool
starvation, instead there is a provided method to asynchronously wait for WaitHandle
instances, this is called ThreadPool.RegisterWaitForSingleObject
.
By using ThreadPool.RegisterWaitForSingleObject
a callback is registered to be invoked when the WaitHandle
is available, unfortunately this is not async/await compatible out of the box, a full implementation which makes this async/await compatible is as follows:
public static class WaitHandleExtensions
{
public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken)
{
return WaitOneAsync(waitHandle, Timeout.Infinite, cancellationToken);
}
public static async Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeout, CancellationToken cancellationToken)
{
// A Mutex can't use RegisterWaitForSingleObject as a Mutex requires the wait and release to be on the same thread
// but RegisterWaitForSingleObject acquires the Mutex on a ThreadPool thread.
if (waitHandle is Mutex)
throw new ArgumentException(StringResources.MutexMayNotBeUsedWithWaitOneAsyncAsThreadIdentityIsEnforced, nameof(waitHandle));
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, OnWaitOrTimerCallback, tcs, timeout, true);
var cancellationCallback = BuildCancellationCallback(rwh, tcs);
using (cancellationToken.Register(cancellationCallback))
{
try
{
return await tcs.Task.ConfigureAwait(false);
}
finally
{
rwh.Unregister(null);
}
}
}
private static Action BuildCancellationCallback(RegisteredWaitHandle rwh, TaskCompletionSource<bool> tcs)
{
return () =>
{
if (rwh.Unregister(null))
{
tcs.SetCanceled();
}
};
}
private static void OnWaitOrTimerCallback(object state, bool timedOut)
{
var taskCompletionSource = (TaskCompletionSource<bool>)state;
taskCompletionSource.SetResult(!timedOut);
}
}
The only limitation is that this cannot be used with a Mutex
.
This can be used like so:
await myAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);
myAutoResetEvent.WaitOne()
for this thread to be blocked. If the constructornew WebServer(myAutoResetEvent)
is blocking the thread, why do you need themyAutoResetEvent.WaitOne()
? – HeliocentricSet
method will be called. – RigdonSet()
– RigdonmyAutoResetEvent.WaitOne()
tells the system to block the current thread till themyAutoResetEvent.Set()
is called.myAutoResetEvent.WaitOne()
serves no other purpose, or does it? What I mean to say is, ifmyAutoResetEvent.WaitOne()
is blocking and you don't want it to block, you should simply delete that line. Am I missing something here? – HeliocentricSet()
is called but without blocking the thread. – Nehru