Igo*_*pov 9 c# asynchronous async-await
我试图了解如何以及何时使用async编程并进行I/O绑定操作,但我不理解它们.我想从头开始实现它们.我怎样才能做到这一点?
考虑下面的同步示例:
private void DownloadBigImage() {
var url = "https://cosmos-magazine.imgix.net/file/spina/photo/14402/180322-Steve-Full.jpg";
new WebClient().DownloadFile(url, "image.jpg");
}
Run Code Online (Sandbox Code Playgroud)
我如何async通过只使用正常的同步方法DownloadBigImage 来Task.Run实现该版本而不使用,因为这将仅使用线程池中的线程进行等待 - 这只是浪费!
也不要使用已经有的特殊方法async!这就是这个问题的目的:如何在不依赖已经异步的方法的情况下自己制作它?所以,没有像这样的事情:
await new WebClient().DownloadFileTaskAsync(url, "image.jpg");
Run Code Online (Sandbox Code Playgroud)
在这方面非常缺乏可用的示例和文档.我发现只有这个:https: //docs.microsoft.com/en-us/dotnet/standard/async-in-depth ,其中说:
对GetStringAsync()的调用通过较低级别的.NET库(可能调用其他异步方法)调用,直到它到达本机网络库的P/Invoke互操作调用.本机库随后可以调用System API调用(例如对Linux上的套接字的write()).将在本机/托管边界创建任务对象,可能使用TaskCompletionSource.任务对象将通过层传递,可能在操作或直接返回,最终返回到初始调用者.
基本上我必须使用" P/Invoke互操作调用到本机网络库 "......但是如何?
我认为这是一个非常有趣的问题,也是一个有趣的学习练习.
从根本上说,您不能使用任何同步的现有API.一旦它同步,就无法将其变为真正的异步.您正确地识别出它Task.Run并且它的等价物不是解决方案.
如果拒绝调用任何异步.NET API,则需要使用PInvoke调用本机API.这意味着您需要调用WinHTTP API或直接使用套接字.这是可能的,但我没有经验来指导你.
相反,您可以使用异步托管套接字来实现异步HTTP下载.
从同步代码开始(这是一个原始草图):
using (var s = new Socket(...))
{
s.Connect(...);
s.Send(GetHttpRequestBytes());
var response = new StreamReader(new NetworkStream(s)).ReadToEnd();
}
Run Code Online (Sandbox Code Playgroud)
这非常粗略地将您的HTTP响应作为字符串.
您可以通过使用轻松实现真正的异步await.
using (var s = new Socket(...))
{
await s.ConnectAsync(...);
await s.SendAsync(GetHttpRequestBytes());
var response = await new StreamReader(new NetworkStream(s)).ReadToEndAsync();
}
Run Code Online (Sandbox Code Playgroud)
如果你考虑await就你的运动目标作弊,你需要使用回调来写这个.这太糟糕了,所以我只想写连接部分:
var s = new Socket(...)
s.BeginConnect(..., ar => {
//perform next steps here
}, null);
Run Code Online (Sandbox Code Playgroud)
同样,这段代码非常原始,但它显示了原理.Connect您可以注册IO完成时调用的回调,而不是等待IO完成(这隐式发生在内部).这样你的主线程继续运行.这会把你的代码变成意大利面条.
您需要使用回调编写安全处理.这是一个问题,因为异常处理不能跨越回调.此外,如果您不想依赖框架来执行此操作,则可能需要编写读取循环.异步循环可以是弯曲的.
这是一个很好的问题,在大多数关于 C# 和 async 的文本中并没有很好地解释。
我搜索了很长时间,认为我可以而且应该实现我自己的异步 I/O 方法。如果我使用的方法/库没有异步方法,我想我应该以某种方式将这些函数包装在使它们异步的代码中。事实证明,这对于大多数程序员来说并不是真正可行的。是的,您可以使用生成一个新线程Thread.Start(() => {...}),这确实使您的代码异步,但它也会创建一个新线程,这对于异步操作来说是一个昂贵的开销。它当然可以释放您的 UI 线程以确保您的应用程序保持响应,但它不会像 HttpClient.GetAsync() 是真正的异步操作那样创建真正的异步操作。
这是因为 .net 库中的异步方法使用称为“.NET 中的标准 P/Invoke 异步 I/O 系统”的东西来调用在执行出站 IO(网络或存储)时不需要专用 CPU 线程的低级 OS 代码)。它实际上并没有将线程专用于其工作,而是在完成其工作后向 .net 运行时发出信号。
我不熟悉细节,但这些知识足以让我免于尝试实现异步 I/O,并使我专注于使用 .net 库中已经存在的异步方法(例如 HttpClient.GetAsync())。可以在此处找到更多有趣的信息(Microsoft 异步深入研究)以及Stephen Cleary的精彩描述此处
| 归档时间: |
|
| 查看次数: |
475 次 |
| 最近记录: |