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

编程入门 行业动态 更新时间:2024-10-11 17:26:15
本文介绍了使用Lazy< T>实现AsyncManualResetEvent.确定是否已等待任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在基于 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&lt; T&gt;实现AsyncManualResetEvent.确定是否已等待任务

本文发布于:2023-11-13 23:00:21,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1585558.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:amp   Lazy   lt   AsyncManualResetEvent   gt

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!