ManualResetEventSlim represents a thread synchronization event that must be reset manually after being signaled. It is a common synchronization primitive, but it does not expose an asynchronous wait method. Fortunately, creating an extension method using ThreadPool.RegisterWaitForSingleObject and TaskCompletionSource is straightforward.
C#
static class Extensions
{
public static Task WaitAsync(this ManualResetEventSlim manualResetEvent, CancellationToken cancellationToken = default)
=> WaitAsync(manualResetEvent.WaitHandle, cancellationToken);
public static Task WaitAsync(this WaitHandle waitHandle, CancellationToken cancellationToken = default)
{
CancellationTokenRegistration cancellationRegistration = default;
var tcs = new TaskCompletionSource();
var handle = ThreadPool.RegisterWaitForSingleObject(
waitObject: waitHandle,
callBack: (o, timeout) =>
{
cancellationRegistration.Unregister();
tcs.TrySetResult();
},
state: null,
timeout: Timeout.InfiniteTimeSpan,
executeOnlyOnce: true);
if (cancellationToken.CanBeCanceled)
{
cancellationRegistration = cancellationToken.Register(() =>
{
handle.Unregister(waitHandle);
tcs.TrySetCanceled(cancellationToken);
});
}
return tcs.Task;
}
}
You can test these extension methods using the following code:
C#
var manualResetEvent = new ManualResetEventSlim();
_ = Task.Run(async () =>
{
await Task.Delay(2_000);
Console.WriteLine("Unlock");
manualResetEvent.Set();
});
await manualResetEvent.WaitAsync(cts.Token);
Console.WriteLine("Done");
#Additional resources
Do you have a question or a suggestion about this post? Contact me!