【问题标题】:Asynchronous network programming with async/await使用 async/await 进行异步网络编程
【发布时间】:2015-03-16 12:49:56
【问题描述】:

在过去的几年中,我使用异步编程模型和套接字开发了客户端/服务器软件。 MSDN 上的这个 example 尽管与 ManualResetEvents 等同步机制过于复杂,但说明了这些概念:您使用 BeginXXX()EndXXX() 方法对进行连接和流操作。

这样做的好处是让线程池根据需要(例如,当接收到数据时)分配线程,而不是为每个连接分配一个专用线程,这无法扩展。

最近有人提到,这种方法也可以使用 .NET 4.5 中引入的 async/await 模型来实现,因此在这种情况下使用 Tasks 并使 APM 变得不必要。这是怎么做到的?

【问题讨论】:

  • 这是怎么做的你问的是async-await的实现细节吗?
  • 我在问如何使用 TAP 而不是 APM 来解决相同的问题(即实现异步网络编程)。
  • 恐怕不行;答案太笼统了,并没有解决我所问的网络编程的具体问题。也许您可以将其添加到您的答案中,在这种情况下就可以了。
  • 您应该更具体地说明您所指的网络编程。
  • 如果您能说明我的问题不清楚的地方,我会很乐意相应地安排。

标签: c# asynchronous network-programming client-server async-await


【解决方案1】:

Task Asynchronous Pattern 及其使用的关键字 async-await 让您可以使用异步 I/O(如您所说),但以“更清洁”的方式。

除了使用BeginXXXEndXXX 方法并在彼此之间传递回调,您可以简单地在返回等待的异步方法上使用awaitTask 就是其中的一个示例)。

例如,让我们用HttpClient做一个例子:

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

一旦方法命中await关键字,它将把控制权交还给调用方法,在幕后创建一个状态机(它将负责执行延续,传播异常,等)并在 IO 工作完成后通过 IOCP(IO 完成端口,由 ThreadPool 分配)恢复。

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);

编辑:

async的使用方法很简单。例如,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;
}

您将方法标记为async,以便您可以在其中使用await 关键字,然后在读取的方法上简单地使用await

【讨论】:

  • 我知道 TAP 是如何工作的;我只需要知道它是如何应用于网络编程的(例如:在 APM 中,您递归调用 BeginRead() 来接收数据;使用 TAP 是如何完成的)?
  • 您的示例仍然是关于文件处理的(而我专门询问了网络编程)。此外,您很可能无法轻松地使用 await 来读取数据,因为您会阻止同时处理多条消息。
  • @Gigi 为什么你认为你不能同时阅读多条消息?此外,API 本身不是问题,而是您使用async 的方式。如果您想使用特定的库,您应该这样说
  • @Gigi BTW - 我的第一个示例使用HttpClient。如果那不是您想要的,那么您需要更好地解释自己。
【解决方案2】:

其实Socket类支持的旧APM使用了一个特殊的线程池,IOCP(“I/O Completion Port”)线程池,而不是“收到数据时”分配线程,实际上是分配线程因为 I/O 操作是启动的,但是以一种允许单个线程(或少量线程……例如,与系统上的内核一样多)处理大量套接字对象的方式和操作。

至于如何使用新的async/await 样式API,不幸的是Socket 类没有得到任何直接async 的喜爱。然而,所有的包装器都做到了。最直接的替换是使用NetworkStream 类,并使用ReadAsync()WriteAsync(),它们是可等待的方法。

如果您仍想(基本上)直接使用Socket API,您必须自己使用可等待的实现来包装它。恕我直言,这里是一个很好的资源,可以解释一种可能的方法:Awaiting Socket Operations

【讨论】:

    猜你喜欢
    • 2016-12-04
    • 2013-04-11
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多