简体   繁体   English

使用异步/等待进行异步网络编程

[英]Asynchronous network programming with async/await

For the past few years, I've developed client/server software using the Asynchronous Programming Model and sockets. 在过去的几年中,我已经使用异步编程模型和套接字开发了客户端/服务器软件。 This example on MSDN, although overcomplicated with synchronisation mechanisms such as ManualResetEvents, illustrates the concepts: you use a BeginXXX() and EndXXX() method pair for the connection and stream operations. MSDN上的此示例尽管复杂化了诸如ManualResetEvents之类的同步机制,但却说明了以下概念:您将BeginXXX()EndXXX()方法对用于连接和流操作。

This has the advantage of letting the thread pool assign threads as needed (eg when data is received) rather than having a dedicated thread per connection, which doesn't scale. 这样的好处是让线程池根据需要(例如,当接收到数据时)分配线程,而不是每个连接都没有专用线程,这不会扩展。

Someone recently mentioned that this approach can also be achieved using the async/await model introduced in .NET 4.5, thus using Tasks and making the APM unnecessary for this scenario. 最近有人提到,也可以使用.NET 4.5中引入的async / await模型来实现此方法,从而使用Tasks并使该情况下不需要APM。 How is this done? 怎么做?

The Task Asynchronous Pattern and its used keywords async-await lets you take use of async I/O (as you said) but in a "cleaner" fashion. Task Asynchronous Pattern及其使用的关键字async-await允许您以“更干净”的方式使用异步I / O(如您所说)。

Instead of using BeginXXX and EndXXX methods and passing callbacks between each other, you can simply await on an async method which returns an awaitable (A Task is an example of one). 无需使用BeginXXXEndXXX方法并在彼此之间传递回调,您只需await返回awaitable的async方法上await即可(A Task是其中的一个示例)。

For example, lets make an example using HttpClient : 例如,让我们使用HttpClient进行示例:

public async Task DoGetWebRequestAsync()
{
    var httpClient = new HttpClient();
    await httpClient.GetAsync(url);
}

What this will do is once the method hits the await keyword, it will yield control back to the calling method, creating a state-machine behind the scenes (which will be in charge of executing the continuation, propagating exceptions, etc..) and resume via an IOCP (IO Completion port, allocated by the ThreadPool) once the IO work is complete. 要做的是,一旦该方法命中了await关键字,它将把控制权交还给调用方法,在幕后创建一个状态机(该状态机将负责执行继续,传播异常等),并IO工作完成后,可通过IOCP(由ThreadPool分配的IO完成端口)恢复。

The TAP also offers a way to turn the old APM pattern into the TAP using TaskFactory.FromAsync : TAP还提供了一种使用TaskFactory.FromAsync将旧的APM模式转换为TAP的方法:

FileInfo fi = new FileInfo(path);
byte[] data = null;
data = new byte[fi.Length];

FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read,
                               FileShare.Read, data.Length, true);

//Task<int> returns the number of bytes read
Task<int> task = Task<int>.Factory.FromAsync(
        fs.BeginRead, fs.EndRead, data, 0, data.Length, null);

Edit: 编辑:

How to use async is simple. 如何使用async很简单。 For example, with FileStream : 例如,使用FileStream

public async Task<byte[]> ReadBufferAsync(string path)
{
    FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read,
                                FileShare.Read, data.Length, true);

    // Read some bytes, not all, just for the example.
    byte[] buffer = new byte[2048];
    await fs.ReadAsync(buffer, 0, buffer.Length);

    return buffer;
}

You mark you method async so you can use the await keyword inside it, and then simply await on the method which reads. 您将方法标记为async以便可以在其中使用await关键字,然后仅在读取的方法上await

Actually, the old APM supported by the Socket class used a special thread pool, the IOCP ("I/O Completion Port") thread pool, and rather than assigning threads "when data is received", actually assigned threads as I/O operations are initiated , but in a way that allows a single thread (or small number of threads…eg as many as there are cores on the system) to handle a large number of socket objects and operations. 实际上,由Socket类支持的旧APM使用了特殊的线程池IOCP(“ I / O完成端口”)线程池,而不是在“接收数据时”分配线程,而是将线程实际分配为I / O操作启动 ,但以允许单个线程(或少量线程,例如与系统上的内核一样多的线程)的方式处理大量套接字对象和操作。

As far as how to use the new async / await style API, unfortunately the Socket class didn't get any direct async love. 至于如何使用新的async / await样式的API,不幸的是Socket类没有得到任何直接的async爱。 However, all of the wrappers did. 但是,所有包装程序都做到了。 The most straight-forward replacement is to use the NetworkStream class, and use ReadAsync() and WriteAsync() , which are awaitable methods. 最简单的替换方法是使用NetworkStream类,并使用ReadAsync()WriteAsync() ,它们是可以等待的方法。

If you want to still use the Socket API (essentially) directly, you'll have to wrap it yourself with an awaitable implementation. 如果您仍然想直接使用Socket API(本质上),则必须使用等待的实现将其包装起来。 IMHO, a very good resource explaining one possible approach for doing this is here: Awaiting Socket Operations 恕我直言,这里是一个很好的资源,它解释了一种可行的方法: 等待套接字操作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM