【问题标题】:Using Async methods inside IAsyncResult在 IAsyncResult 中使用异步方法
【发布时间】:2016-10-03 20:37:15
【问题描述】:

我正在从 Microsoft.ServiceBus 命名空间实现 TokenProvider 类。 我正在覆盖以下方法

protected override IAsyncResult OnBeginGetToken(string appliesTo, string action, TimeSpan timeout, AsyncCallback callback,
            object state)
{
    var token = GetCustomTokenAsync(appliesTo); //How to await?
     return new CompletedAsyncResult<SharedAccessSignatureToken>(token, callback, state);
}

但正如上面评论中提到的那样。 GetCustomTokenAsync 是一种异步方法,我如何等待而不干扰 TokenProvider 类的签名?我想利用 GetCustomTokenAsync 的异步特性,因此我不愿意使用.Result。有没有更好的办法解决这个问题?

【问题讨论】:

标签: c# asynchronous async-await task-parallel-library


【解决方案1】:

你需要create APM wrappers around a TAP method

这并不完全简单,尤其是因为 TokenProviderEnd* 方法遵循 APM 模式。

要求和建议:

  • Task 实现了IAsyncResult,所以你可以返回一个任务。
  • state 成员必须从该任务的 IAsyncResult.AsyncState 属性返回,因此您返回的任务不能是由 async 生成的 - 您必须通过自己创建它TaskCompletionSource&lt;T&gt;
  • 操作完成时需要调用callback
  • 如果您覆盖Begin*,您必须也覆盖匹配的End*

生成的代码最终会有点复杂。您可以通过在我的AsyncEx.Tasks 库中使用ApmAsyncFactory.ToBegin 来避免样板(目前,ApmAsyncFactory 仅在prerelease build 中):

protected override IAsyncResult OnBeginGetToken(string appliesTo, string action,
    TimeSpan timeout, AsyncCallback callback, object state)
{
  return ApmAsyncFactory.ToBegin(GetCustomTokenAsync(appliesTo), callback, state);
}

protected override SecurityToken OnEndGetToken(IAsyncResult result,
    out DateTime cacheUntil)
{
  var ret = ApmAsyncFactory.ToEnd<SharedAccessSignatureToken>(result);
  cacheUntil = ...;
  return ret;
}

或者,如果您想了解香肠是如何制作的并全部手工完成,那么一种选择是(记住所有例外情况都可能发生在哪里):

protected override IAsyncResult OnBeginGetToken(string appliesTo, string action,
    TimeSpan timeout, AsyncCallback callback, object state)
{
  var tcs = new TaskCompletionSource<SharedAccessSignatureToken>(state,
      TaskCreationOptions.RunContinuationsAsynchronously);
  var _ = CompleteAsync(GetCustomTokenAsync(appliesTo), callback, tcs);
  // _ is ignored; it can never fault.
  return tcs.Task;
}

private static async Task CompleteAsync<TResult>(Task<TResult> task,
    AsyncCallback callback, TaskCompletionSource<TResult> tcs)
{
  try
  {
    tcs.TrySetResult(await task.ConfigureAwait(false));
  }
  catch (OperationCanceledException ex)
  {
    tcs.TrySetCanceled(ex.CancellationToken);
  }
  catch (Exception ex)
  {
    tcs.TrySetException(ex);
  }
  finally
  {
    // Invoke callback unsafely on the thread pool, so exceptions are global
    if (callback != null)
      ThreadPool.QueueUserWorkItem(state => callback((IAsyncResult)state), tcs.Task);
  }
}

protected override SecurityToken OnEndGetToken(IAsyncResult result,
    out DateTime cacheUntil)
{
  var task = (Task<SharedAccessSignatureToken>)result;
  var ret = task.GetAwaiter().GetResult();
  cacheUntil = ...;
  return ret;
}

【讨论】:

  • Task没有实现IAsyncResult吗?为什么不使用ContinueWith 映射到CompletedAsyncResult 并返回该任务?
  • ContinueWith 返回的任务将没有正确的IAsyncResult.AsyncState。此外,完全使用CompletedAsyncResult 会使实现同步而不是异步。
【解决方案2】:

我根本不会使用IAsyncResult,而是你应该返回一个Task&lt;T&gt; 并等待它。您的 GetCustomTokenAsync 方法也是如此。

【讨论】:

  • 这对实现继承自TokenProvider 的类没有帮助,这是这里的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多