使用 Lazy<T> 实现 AsyncManualResetEvent 以确定任务是否已等待
Implement AsyncManualResetEvent using Lazy<T> to determine if the task has been awaited
我正在实施基于 Stephen Toub's example 的 AsyncManualResetEvent。但是,我想知道事件,或者具体来说,底层 Task<T>
是否已经等待。
我已经调查了 Task
class,但似乎没有一种明智的方法来确定它是否曾经 'awaited' 或者是否有延续已添加。
然而,在这种情况下,我控制对底层任务源的访问,因此我可以监听对 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;
}
那么我的问题是,这是否是一种安全的方法,具体来说,这是否可以保证在重置事件时永远不会有任何 orphaned/lost 继续?
如果您真的想知道是否有人在您的任务中调用了 await
(而不仅仅是他们调用了 WaitAsync()
的事实),您可以制作一个自定义等待程序作为 await
的包装器=14=] 被 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
。
我还添加了TaskCreationOptions.RunContinuationsAsynchronously
来解决斯蒂芬在文章末尾用Task.Factory.StartNew
修复的问题(它是在文章写完后引入.NET的)。
如果你只是想看看是否有人调用 WaitAsync 你可以简化它很多,你只需要一个 class 来保存你的标志和你的完成源。
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; }
}
}
我正在实施基于 Stephen Toub's example 的 AsyncManualResetEvent。但是,我想知道事件,或者具体来说,底层 Task<T>
是否已经等待。
我已经调查了 Task
class,但似乎没有一种明智的方法来确定它是否曾经 'awaited' 或者是否有延续已添加。
然而,在这种情况下,我控制对底层任务源的访问,因此我可以监听对 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;
}
那么我的问题是,这是否是一种安全的方法,具体来说,这是否可以保证在重置事件时永远不会有任何 orphaned/lost 继续?
如果您真的想知道是否有人在您的任务中调用了 await
(而不仅仅是他们调用了 WaitAsync()
的事实),您可以制作一个自定义等待程序作为 await
的包装器=14=] 被 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
。
我还添加了TaskCreationOptions.RunContinuationsAsynchronously
来解决斯蒂芬在文章末尾用Task.Factory.StartNew
修复的问题(它是在文章写完后引入.NET的)。
如果你只是想看看是否有人调用 WaitAsync 你可以简化它很多,你只需要一个 class 来保存你的标志和你的完成源。
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; }
}
}