- Asynchronous
 - No need to dispose
 - Allocation-free most of the time
 
var locker = new AsyncLock();     
using (await locker.LockAsync())
{
    // ...
}var lazy = new AsyncLazy<int>(async () => await Task.FromResult(123), cacheFailure: false);
int value = await lazy.GetValueAsync();- Asynchronous
 - No need to dispose
 - Supports timeout
 - Supports cancellation
 
var are = new AsyncAutoResetEvent();            
_ = Task.Delay(3000).ContinueWith(_ => are.Set());
bool gotSignal = await are.WaitAsync(5000, CancellationToken.None);Skips redundant method calls
- Guarantees synchronization between callback and Dispose call
 
using (var throttle = new Throttle<int>(callback: p => UiShowProgress(p), 100))
{
    for (int i = 0; i <= 100; i++)
    {
        throttle.Invoke(i);
        Thread.Sleep(10);
    }
}
// At this point, we know that "callback" is completed and will no longer be called by the Throttle.
UiShowProgress(100); // Just making sure that the user sees that the operation is 100% completed.
static void UiShowProgress(int progress)
{
    Console.WriteLine(progress); // Results: 8, 17, 25, 33, .., 100.
}