如何在c#中创建和使用自定义Awaitable?

时间:2012-11-17 11:50:20

标签: c# .net async-await

我正在尝试实现一个自定义awaiteable来执行await Thread.SleepAsync()而不创建任何其他线程。

这就是我所拥有的:

    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();
            }
        }
    }

以下是睡眠的工作原理:

    static async Task Sleep(int milliseconds)
    {
        await new AwaitableThread(milliseconds);
    }

问题是这个函数不可避免地返回,即使在OnCompleted中,IsCompleted仍然是假的。

我做错了什么?

1 个答案:

答案 0 :(得分:10)

完全实现生产使用的等待模式是一件棘手的事情 - 您需要捕获执行上下文等等。 Stephen Toub's blog post就此而言有更多细节。在许多情况下,可能更容易背驮到Task<T>Task,可能会使用TaskCompletionSource。例如,在您的情况下,您可以像这样编写等效的Task.Delay

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;
}

您现在可以await完成该任务,就像您可以等待Task.Delay的结果一样。