TPL的TaskFactory.FromAsync与使用阻塞方法的任务

20

我想知道在使用TPL TaskFactory.FromAsyncTaskFactory.StartNew 在阻塞方法上的性能影响是否有所不同。我正在编写一个TCP服务器,最多支持100个并发连接。使用第一种选项,并将多个读取和写入操作链接起来后,我得到了难以调试的代码。

我相信使用同步版本的代码,并用任务(Task)包装它会降低复杂性并增加可测试性,但我担心这样做会带来性能方面的影响。

例如,这两个调用之间是否存在性能差异:

NetworkStream stream;
byte[] data;
int bytesRead;

//using FromAsync
Task<int> readChunk = Task<int>.Factory.FromAsync (
      stream.BeginRead, stream.EndRead,
      data, bytesRead, data.Length - bytesRead, null);

//using StartNew with blocking version
Task<int> readChunk2 = Task<int>.Factory.StartNew(() => 
      stream.Read(data, bytesRead, data.Length - bytesRead));
2个回答

45
你绝对需要在API提供BeginXXX/EndXXX版本的方法时使用FromAsync。区别在于,在像Stream、Socket或WebRequest这样的情况下,您实际上将在底层使用异步I/O(例如Windows上的I/O完成端口),这比阻塞多个CPU线程执行同步操作要高效得多。这些方法提供了实现I/O可扩展性的最佳方式。
请查看MSDN上.NET SDK的TPL和传统.NET异步编程部分,以获取有关如何将这两种编程模型结合使用以实现异步nirvana的更多信息。

1
你有任何来源可以证明使用Begin/End与Async调用Web方法(例如)比使用同步版本和Async更好吗?你所说的很有道理,但我想在官方文章中读到相关内容。 - Kevin Kalitowski

5

以下是从外部链接复制的内容:

是的。在.NET 4中,任务并行库包括了一个内置的APM模式(Begin/End)包装器:Task.Factory.FromAsync。例如,如果您想为调用Stream的BeginRead/EndRead方法创建一个任务,可以这样做:

Stream s = ...;
byte [] buffer = ...;
Task<int> numBytesRead = Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);
// or with await
int numBytesRead = await Task<int>.Factory.FromAsync(s.BeginRead, s.EndRead, buffer, 0, buffer.Length, null);

在底层,FromAsync 只是基于 TaskCompletionSource 构建的。对于这个读取示例来说,一个简单版本的 FromAsync 可能如下所示:
var tcs = new TaskCompletionSource<TResult>();
s.BeginRead(buffer, 0, buffer.Length, iar =>
{
    try { tcs.SetResult(s.EndRead(iar)); }
    catch(Exception exc) { tcs.SetException(exc); }
}, null);
Task<int> numBytesRead = tcs.Task;

http://social.msdn.microsoft.com/Forums/en/async/thread/ed8a14e8-d19a-42d1-bc3f-7017bdfed09c


3
请提供外部链接的内容。否则,如果链接失效,您的答案将毫无价值。 - sra
3
如果 MSDN 网站崩溃了,我们所有人都毫无价值。 - John Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接