微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 IAsyncResult 方法中调用异步任务

如何解决如何在 IAsyncResult 方法中调用异步任务

我有一个从旧代码返回类型为 IAsyncResult 的现有方法,如何从该方法调用异步任务?例如:

public IAsyncResult BeginXXX()
{
    // I want to call my async Task here,but it cannot compile like this:
    var value = await DoMyTaskAsync();
    DoSomethingWithValue(value);

    // existing code
    return new SomeOtherAsyncResult();
}

编辑:解决方案最好不必重构方法签名,这将涉及超出范围的工作。

解决方法

由于 Task 派生自 IAsyncResult

public class Task : IThreadPoolWorkItem,IAsyncResult,IDisposable ...

将重构后的代码包装在一个 Task.Run 中,以便维护当前的 API 并且仍然能够使用 async-await

public IAsyncResult BeginXXX() {        
    return Task.Run(async () => {
        var value = await DoMyTaskAsync(); // I want to call my async Task here
        DoSomethingWithValue(value);

        // existing code
        return new SomeOtherAsyncResult();
    });
}

这应该适用于所示示例的范围。

我强烈建议这只是一个临时修复,因为这可能会导致堆栈死锁,混合异步调用和阻塞调用。

这段代码和依赖它的代码最终应该被重构,以遵循为异步代码提供的通用文档的建议。

,

我有一个从旧代码返回类型为 IAsyncResult 的现有方法,如何从该方法中调用异步任务?

答案在 TAP interop documentation 中。基本上你返回一个 TaskCompletionSource<T>.Task

这里有一些helpers from my AsyncEx library可以让这件事变得更容易:

public static IAsyncResult ToBegin<TResult>(Task<TResult> task,AsyncCallback callback,object state)
{
  var tcs = new TaskCompletionSource<TResult>(state,TaskCreationOptions.RunContinuationsAsynchronously);
  var oldContext = SynchronizationContext.Current;
  SynchronizationContext.Current = null;
  try
  {
    CompleteAsync(task,callback,tcs);
  }
  finally
  {
    SynchronizationContext.Current = oldContext;
  }
  return tcs.Task;
}

// `async void` is on purpose,to raise `callback` exceptions directly on the thread pool.
private static async void CompleteAsync<TResult>(Task<TResult> task,TaskCompletionSource<TResult> tcs)
{
  try
  {
    tcs.TrySetResult(await task.ConfigureAwait(false));
  }
  catch (OperationCanceledException ex)
  {
    tcs.TrySetCanceled(ex.CancellationToken);
  }
  catch (Exception ex)
  {
    tcs.TrySetException(ex);
  }
  finally
  {
    callback?.Invoke(tcs.Task);
  }
}

public static TResult ToEnd<TResult>(IAsyncResult asyncResult) =>
    ((Task<TResult>)asyncResult).GetAwaiter().GetResult();

请注意,TaskCompletionSource<T> 是为您的 state 参数提供正确语义所必需的。

有了这些,您的实现可能如下所示:

public IAsyncResult BeginXXX(...rest,object state) =>
    XXXAsync(...rest).ToBegin(callback,state);
public EndXXX(IAsyncResult asyncResult) => TaskExtensions.ToEnd<MyResult>(asyncResult);

public async Task<MyResult> XXXAsync(...rest)
{
  var value = await DoMyTaskAsync();
  DoSomethingWithValue(value);
  return myResult;
}

请注意,这里假设您要将这个级别的所有内容都翻译成 async/await 并去掉 new SomeOtherAsyncResult。但是,如果您没有准备好转换为 IAsyncResult/async 的较低级别的基于 await 的 API 并且仍然需要调用,那么您的 {{1} } 方法可以使用TAP-over-APM

XXXAsync

在这种情况下,您最终会使用 APM-over-TAP(以保留您的 public async Task<MyResult> XXXAsync(...rest) { var value = await DoMyTaskAsync(); DoSomethingWithValue(value); return await TaskFactory.FromAsync(BeginOtherAsyncResult,EndOtherAsyncResult,...restOtherAsyncResultArgs,null); } 接口)和 TAP-over-APM(以继续使用 BeginXXX 接口),并将您的 TAP 方法夹在中间(OtherAsyncResult) 之间。

,

这是一个常见的问题。您可以使用 TaskCompletionSource 解决此问题。

假设您有一个遗留类,它具有旧式异步方法 BeginBarEndBarBeginBar 接受 AsyncCallback 委托。

public Task<int> Foo()
{
    var source = new TaskCompletionSource<int>();
    someLegacyClass.BeginBar( x => source.TrySetResult(someLegacyClass.EndBar(x)); }
    return source.Task;
}

interop with other asynchronous patterns

,

在调用 DoMyTaskAsync 函数时,您“可以”忽略 Asynchronous Programming 中的所有优点并执行以下操作:

public IAsyncResult BeginXXX()
{
    // I want to call my async Task here,but it cannot compile like this:
    var value = DoMyTaskAsync().Result;
    DoSomethingWithValue(value);

    // existing code
    return new SomeOtherAsyncResult();
}

或者,您可以阅读 async 对函数的作用。如果您在调用它时不使用 DoMyTaskAsync,那么所有 await 确实会返回一个 Task<T> 实例。

您可以正常操作Task实例,而无需依赖await。阅读Task-based Asynchronous Programming

,

您的函数定义中缺少 async 关键字。
您还需要返回 Task<T>

尝试这样的事情:

public async Task<IAsyncResult> BeginXXX()
{
    await DoMyTaskAsync();

    // the rest of your code
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。