我正在基于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;
}
我的问题是,这是否是一种安全的方法,特别是这可以保证在重置事件时永远不会有任何孤立/丢失的延续吗?
答案 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