内容简介:.NET 中的本文将讲述如何实现一个可等待对象,一个自定义的 Awaiter。入门篇:
.NET 中的 async
/ await
写异步代码用起来真的很爽,就像写同步一样。我们可以在各种各样的异步代码中看到 Task
返回值,这样大家便可以使用 await
等待这个方法。不过,有时需要写一些特别的异步方法,这时需要自己来实现一个可以异步等待的对象。
本文将讲述如何实现一个可等待对象,一个自定义的 Awaiter。
Awaiter 系列文章
入门篇:
- .NET 中什么样的类是可使用 await 异步等待的?
- 定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便
- .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
实战篇:
可等待对象
我们希望大家在调用下面的 CallWalterlvAsync
方法的时候,可以使用 await
关键字来异步等待:
await CallWalterlvAsync();
public WalterlvOperation CallWalterlvAsync() { // 返回一个 WalterlvOperation,以便外面调用方可以异步等待。 return new WalterlvOperation(); }
所以我们需要实现一个 WalterlvOperation
。
编写基本的 Awaiter 框架代码
先写一个空的类型,然后为它编写一个空的 GetAwaiter
方法,返回新的 WalterlvAwaiter
类型。
/// <summary> /// 委托 walterlv 来完成一项特殊的任务。 /// 通过在代码当中调用,可以让他在现实中为你做一些事情。 /// </summary> public class WalterlvOperation { public WalterlvAwaiter GetAwaiter() { return new WalterlvAwaiter(); } }
接着,我们编写 WalterlvAwaiter
类:
public class WalterlvAwaiter : INotifyCompletion { public bool IsCompleted { get; } public void GetResult() { } public void OnCompleted(Action continuation) { } }
必须实现 INotifyCompletion
接口,此接口带来了 OnCompleted
方法。另外两个方法不是接口带来的,但是也是实现一个自定义的 Awaiter
必要的方法。
在你编写完以上两段代码之后,你的 await
就可以编译通过了。
额外说明一下, GetResult
方法是可以修改返回值的,只要返回值不是 void
,那么 await
等待的地方将可以在 await
完成之后获得一个返回值。
public class WalterlvAwaiter : INotifyCompletion { public bool IsCompleted { get; } public string GetResult() { } public void OnCompleted(Action continuation) { } }
// 于是你可以拿到一个字符串类型的返回值。 string result = await CallWalterlvAsync("写博客");
实现基本的 Awaiter
以上代码只能编译通过,但实际上如果你跑起来,会发现 await
一旦进入,是不会再往下执行的。因为我们还没有实现 WalterlvAwaiter
类型。
最重要的,是需要调用 OnCompleted
方法传入的 continuation
委托。
public void OnCompleted(Action continuation) { continuation.Invoke(); }
像以上这么写之后, await
之后的代码便可以执行了。
如果你只是希望了解如何实现一个 Awaiter,那么写出以上的代码就足以。因为这才是最本质最核心的 Awaiter 的实现。
不过,以上代码的执行是立即执行,没有任何异步的效果。因为 OnCompleted
被调用的时候,我们立刻调用了 continuation
的执行。
实现异步的 Awaiter
要真正达到异步的效果, OnCompleted
执行的时候,我们不能立刻去调用参数传进来的委托,而只是将他记录下来,等到任务真正完成的时候再去调用。
以下的代码就不再是通用的代码了,你需要针对你的不同业务去设计如何异步完成一个任务,然后再通知到异步等待的代码继续执行。
例如,现在我们期望 walterlv 代理去写博客,于是我们为 WalterlvOperation
加一点功能,真正去做一些异步的事情。
CallWalterlvAsync
的实现现在真的开启了一个异步操作。
public WalterlvOperation CallWalterlvAsync(string task) { var operation = new WalterlvOperation(task); operation.Start(); return operation; }
然后为了实现我们自己添加的 Start
方法,我们在里面去做一些事情。里面第一句就离开了当前线程前往线程池中的其他线程去执行 Console.WriteLine
了。
/// <summary> /// 委托 walterlv 来完成一项特殊的任务。 /// 通过在代码当中调用,可以让他在现实中为你做一些事情。 /// </summary> public class WalterlvOperation { private readonly string _task; private readonly WalterlvAwaiter _awaiter; public WalterlvOperation(string task) { _task = task; _awaiter = new WalterlvAwaiter(); } public async void Start() { await Task.Delay(100).ConfigureAwait(false); Console.WriteLine($"walterlv 已经收到任务:{_task}"); Console.WriteLine($"开始执行"); await Task.Delay(2000).ConfigureAwait(false); Console.WriteLine($"walterlv 已经完成 {_task}。"); _awaiter.ReportCompleted(); } /// <summary> /// 返回一个可等待对象,以便能够使用 await 关键字进行异步等待。 /// </summary> public WalterlvAwaiter GetAwaiter() { return _awaiter; } }
于是现在可以通过下面的代码来要求 walterlv 去写博客了。
await CallWalterlvAsync("写博客");
然而实际上,我们上面还留了一个 _awaiter.ReportCompleted
方法没有实现。由于我们的操作全部是异步的了,这个方法的实现就是为了通知所有正在使用 await
等待的代码,异步任务完成了,可以继续往后面执行了。
public class WalterlvAwaiter : INotifyCompletion { private Action _continuation; public bool IsCompleted { get; private set; } public void GetResult() { // 这个函数我们暂时还没有真正实现,因为需要进行同步等待比较复杂。 // 我们将在本文后面附的其他博客中实现。 } public void OnCompleted(Action continuation) { // 当这个 Awaiter 被 await 等待的时候,此代码会被调用。 // 每有一处 await 执行到,这里就会执行一次,所以在任务完成之前我们需要 +=。 if (IsCompleted) { continuation?.Invoke(); } else { _continuation += continuation; } } public void ReportCompleted() { // 由 WalterlvOperation 来通知这个任务已经完成。 IsCompleted = true; var continuation = _continuation; _continuation = null; continuation?.Invoke(); } }
现在运行程序,会按照异步任务来执行,可以异步等待:
static async Task Main(string[] args) { await CallWalterlvAsync("写博客"); Console.Read(); }
本文会经常更新,请阅读原文: https://walterlv.com/post/understand-and-write-custom-awaiter.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。
以上所述就是小编给大家介绍的《.NET 除了用 Task 之外,如何自己写一个可以 await 的对象?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- C++ 函数可以直接返回一个对象吗?
- 厉害了!这款AI照片工具可以一键添加、删除和编辑对象
- 100人运维200个数据中心,可以吗?华为可以!
- Windows 上的应用程序在运行期间可以给自己改名(可以做 OTA 自我更新)
- c# – 为什么具有受保护修饰符的函数可以被覆盖并且可以在每个位置访问?
- 可以学习的电脑
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Impractical Python Projects
Lee Vaughan / No Starch Press / 2018-11 / USD 29.95
Impractical Python Projects picks up where the complete beginner books leave off, expanding on existing concepts and introducing new tools that you’ll use every day. And to keep things interesting, ea......一起来看看 《Impractical Python Projects》 这本书的介绍吧!