I'm trying to implement a custom awaiteable to execute await Thread.SleepAsync()
without creating any additional threads.
Here's what I've got:
class AwaitableThread : INotifyCompletion
{
public AwaitableThread(long milliseconds)
{
var timer = new Timer(obj => { IsCompleted = true; }, null, milliseconds, Timeout.Infinite);
}
private bool isCompleted = false;
public bool IsCompleted
{
get { return isCompleted; }
set { isCompleted = value; }
}
public void GetResult()
{}
public AwaitableThread GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (continuation != null)
{
continuation();
}
}
}
And here's how the sleep would work:
static async Task Sleep(int milliseconds)
{
await new AwaitableThread(milliseconds);
}
The problem is that this function returns immidiatly, even though in OnCompleted
, IsCompleted
is still false.
What am I doing wrong?
Fully implementing the awaitable pattern for production use is a tricky business - you need to capture the execution context, amongst other things. Stephen Toub's blog post on this has a lot more detail. In many cases, it's easier to piggy-back onto Task<T>
or Task
, potentially using TaskCompletionSource
. For example, in your case, you could write the equivalent of Task.Delay
like this:
public Task MyDelay(int milliseconds)
{
// There's only a generic TaskCompletionSource, but we don't really
// care about the result. Just use int as a reasonably cheap version.
var tcs = new TaskCompletionSource<int>();
Timer timer = new Timer(_ => tcs.SetResult(0), null, milliseconds,
Timeout.Infinite);
// Capture the timer variable so that the timer can't be garbage collected
// unless the task is (in which case it doesn't matter).
tcs.Task.ContinueWith(task => timer = null);
return tcs.Task;
}
You can now await
that task, just like you can await the result of Task.Delay
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With