SslStream.ReadAsync从不调用InnerStream.ReadAsync

Ale*_*lex 2 .net c# async-await

我不确定我是否遗漏了某些东西,或者SslStream中确实存在设计缺陷(可能还有其他可以包装内部流的流类).

考虑这个伪代码:

MyStream
{
  Task<int> ReadAsync()
  { ... }
}

...

{
  MyStream my = new MyStream();
  SslStream ssl = new SslStream(my);
  ssl.ReadAsync(...);
}
Run Code Online (Sandbox Code Playgroud)

虽然可以预期SslStream.ReadAsync最终将调用MyStream.ReadAsync,但它不会.相反,它会调用MyStream.BeginRead(如果已定义).如果未定义MyStream.BeginRead,则行为将难以预测(它将取决于从哪个类派生MyStream等等).

简而言之,为了使SslStream的async/await方法按预期工作,需要实现内部流类的BeginXXX/EndXXX(非异步/等待方法).

BeginXXX/EndXXX模式比async/await模式更复杂(对我来说,这就是引入async/await的原因 - 使异步编程更容易).但是仍然需要开发BeginXXX/EndXXX方法会失去async/await的目的.

而且,需要知道SslStream类的内部实现(因为如果实现不同,它可以直接调用InnerStream.ReadAsync).我的意思是SslStream的公共签名并没有清楚地向我提供关于我是否应该在我的内部流类中实现ReadAsync或BeginRead的足够信息.

为此,我需要使用试错法或检查SslStream的源代码(及其Stream父作为SslStream从基本Stream类继承ReadAsync).这似乎不是一种可靠而直接的编写代码的方法.

当前实现ass/await方法如SslStream/Stream类中的ReadAsync是有原因的吗?

Ste*_*ary 5

是的,Stream特别是有点搞砸了,因为它早在async/ 之前就存在了await.例如,默认实现ReadAsync将实际上在线程池线程上执行阻塞读取.

我建议您ReadAsync作为常规TAP方法覆盖,并且BeginRead/ EndRead作为该TAP方法的APM包装器.在MSDN文档有这个(正确处理的最佳模式callbackstate),但我更愿意调整他们一下,让任何异常EndRead不裹在AggregateException:

public static IAsyncResult ToBegin<T>(
    Task<T> task, AsyncCallback callback, object state)
{
  var tcs = new TaskCompletionSource<T>(state);
  task.ContinueWith(t =>
  {
    if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions)
    else if (t.IsCanceled) tcs.TrySetCanceled();
    else tcs.TrySetResult(t.Result);

    if (callback != null) callback(tcs.Task);
  }, TaskScheduler.Default);
  return tcs.Task;
}

public static T ToEnd<T>(IAsyncResult result)
{
  // Original MSDN code uses Task<T>.Result
  return ((Task<T>)result).GetAwaiter().GetResult();
}
Run Code Online (Sandbox Code Playgroud)