我正在基于 Stephen Toub的实现一个AsyncManualResetEvent例子.但是,我想知道是否等待了该事件,或者确切地说是底层的Task<T>.
I'm implementing an AsyncManualResetEvent based on Stephen Toub's example. However, I would like to know if the event, or specifically, the underlying Task<T> has been waited on.
我已经研究过Task类,并且似乎没有一种明智的方法来确定是否已经等待"或是否添加了延续.
I've already investigated the Task class, and there doesn't seem to be a sensible way to determine if it has ever been 'awaited' or if a continuation has been added.
但是,在这种情况下,我控制对基础任务源的访问,因此我可以侦听对WaitAsync方法的任何调用.在考虑如何执行此操作时,我决定使用Lazy<T>并仅查看它是否已创建.
In this case however, I control access to the underlying task source, so I can listen for any calls to the WaitAsync method instead. In thinking about how to do this, I decided to use a Lazy<T> and just see if it has been created.
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; }那么我的问题是,这是否是一种安全的方法,特别是这是否可以确保在重置事件时不会有任何孤立的/丢失的延续?
My question then, is whether this is a safe approach, specifically will this guarantee that there are never any orphaned/lost continuations while the event is being reset?
推荐答案如果您真的想知道是否有人在您的任务中呼叫了await(不仅仅是他们呼叫了WaitAsync()的事实),您可以定制一个等候者用作m_tcs.Task使用的TaskAwaiter的包装.
If you truly wanted to know if anyone called await on your task (not just the fact that they called WaitAsync()) you could make a custom awaiter that acts as a wrapper for the TaskAwaiter that is used by 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或UnsafeOnCompleted注册延续,则布尔HasWaiters将变为true.
Now if anyone registered a continuation with OnCompleted or UnsafeOnCompleted the bool HasWaiters will become true.
我还在文章末尾添加了TaskCreationOptions.RunContinuationsAsynchronously来解决Stephen用Task.Factory.StartNew修复的问题(在撰写本文后,它被引入了.NET).
I also added TaskCreationOptions.RunContinuationsAsynchronously to fix the issue Stephen fixes with the Task.Factory.StartNew at the end of the article (It was introduced to .NET after the article was written).
如果您只想查看是否有人调用WaitAsync可以对其进行很多简化,则只需要一个类来保存您的标志和完成源即可.
If you just want to see if anyone called WaitAsync you can simplify it a lot, you just need a class to hold your flag and your completion source.
public class AsyncManualResetEvent { private volatile CompletionWrapper _completionWrapper = new CompletionWrapper(); public Task WaitAsync() { var wrapper = _completionWrapper; wrapper.WaitAsyncCalled = true; return wrapper.Tcs.Task; } public bool WaitAsyncCalled { get { return _completionWrapper.WaitAsyncCalled; } } public void Set() { _completionWrapper.Tcs.TrySetResult(true); } public void Reset() { while (true) { var wrapper = _completionWrapper; if (!wrapper.Tcs.Task.IsCompleted || Interlocked.CompareExchange(ref _completionWrapper, new CompletionWrapper(), wrapper) == wrapper) return; } } private class CompletionWrapper { public TaskCompletionSource<bool> Tcs { get; } = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); public bool WaitAsyncCalled { get; set; } } }更多推荐
使用Lazy< T>实现AsyncManualResetEvent.确定是否已等待任务
发布评论