使用Lazy <t>实现AsyncManualResetEvent以确定是否已等待任务

时间:2016-02-05 19:58:49

标签: c# async-await task-parallel-library lazy-evaluation

我正在基于Stephen Toub's example实现AsyncManualResetEvent。但是,我想知道是否已经等待了事件,或者特别是基础Task<T>

我已经调查了Task类,似乎没有一种合理的方法可以确定它是否曾被“等待”或者是否已经添加了延续。

但是,在这种情况下,我控制对底层任务源的访问,因此我可以监听对WaitAsync方法的任何调用。在考虑如何执行此操作时,我决定使用Lazy<T>并查看是否已创建。

sealed class AsyncManualResetEvent {
    public bool HasWaiters => tcs.IsValueCreated;

    public AsyncManualResetEvent() {
        Reset();
    }

    public Task WaitAsync() => tcs.Value.Task;

    public void Set() {
        if (tcs.IsValueCreated) {
            tcs.Value.TrySetResult(result: true);
        }
    }

    public void Reset() {
        tcs = new Lazy<TaskCompletionSource<bool>>(LazyThreadSafetyMode.PublicationOnly);
    }

    Lazy<TaskCompletionSource<bool>> tcs;
}

我的问题是,这是否是一种安全的方法,特别是这可以保证在重置事件时永远不会有任何孤立/丢失的延续吗?

1 个答案:

答案 0 :(得分:4)

如果您真的想知道是否有人在您的任务上调用await(而不仅仅是他们调用WaitAsync()这一事实),您可以创建一个自定义等待者,作为{{1 } TaskAwaiter使用的。

m_tcs.Task

现在,如果有人使用public class AsyncManualResetEvent { private volatile Completion _completion = new Completion(); public bool HasWaiters => _completion.HasWaiters; public Completion WaitAsync() { return _completion; } public void Set() { _completion.Set(); } public void Reset() { while (true) { var completion = _completion; if (!completion.IsCompleted || Interlocked.CompareExchange(ref _completion, new Completion(), completion) == completion) return; } } } public class Completion { private readonly TaskCompletionSource<bool> _tcs; private readonly CompletionAwaiter _awaiter; public Completion() { _tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); _awaiter = new CompletionAwaiter(_tcs.Task, this); } public CompletionAwaiter GetAwaiter() => _awaiter; public bool IsCompleted => _tcs.Task.IsCompleted; public bool HasWaiters { get; private set; } public void Set() => _tcs.TrySetResult(true); public struct CompletionAwaiter : ICriticalNotifyCompletion { private readonly TaskAwaiter _taskAwaiter; private readonly Completion _parent; internal CompletionAwaiter(Task task, Completion parent) { _parent = parent; _taskAwaiter = task.GetAwaiter(); } public bool IsCompleted => _taskAwaiter.IsCompleted; public void GetResult() => _taskAwaiter.GetResult(); public void OnCompleted(Action continuation) { _parent.HasWaiters = true; _taskAwaiter.OnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { _parent.HasWaiters = true; _taskAwaiter.UnsafeOnCompleted(continuation); } } } OnCompleted注册了续集,那么bool UnsafeOnCompleted将成为HasWaiters

我还添加了true来修复Stephen在文章末尾用TaskCreationOptions.RunContinuationsAsynchronously修复的问题(在文章撰写之后引入了.NET)。

如果你只是想看看是否有人调用WaitAsync你可以简化它,你只需要一个类来保存你的旗帜和你的完成源。

Task.Factory.StartNew