将基于异步的线程包装到基于任务的异步

时间:2014-10-13 11:51:33

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

如何在基于任务的异步方法中包装如下代码?

void ExecuteThreadedAsync(Action a) {
   ThreadPool.QueueUserWorkItem(x=> 
   {
      action();
   });
}

目前,此方法称为:

void Method() {
  var context = GetSomeContext();
  ExecuteThreadedAsync(() => 
  {
     var result = TimeConsumingWebServiceCall();
     context.Result = result;
  });
}

但我想要的是:

async void Method() {
  var context = GetSomeContext();
  await ExecuteTaskBasedAsync(() => 
  {
     var result = TimeConsumingWebServiceCall();
     context.Result = result;
  });
}

或者:

async void Method() {
  var context = GetSomeContext();
  var result = await ExecuteTaskBasedAsync<Result>(() => 
  {
    var result = TimeConsumingWebServiceCall();
    return result;
  });
  context.Result = result;
}

3 个答案:

答案 0 :(得分:3)

这取决于TimeConsumingWebServiceCall()方法的作用 - 从名称来看,我推断它调用了一个Web服务,time consuming由于服务响应缓慢而发生。这是一个IO绑定任务。使用ThreadPool.QueueUserWorkItemTask.Run同步调用IO绑定任务通常是一种反模式。您只是将工作卸载到另一个线程,无论如何都会同步阻止Web服务调用。

Task.Run更适合卸载计算绑定任务。

在您的情况下,您应该调查TimeConsumingWebServiceCall实际调用Web服务的方式,并且您应该使用异步IO API。

  1. 您使用的是WCF吗?创建代理时选择Generate task-based operations(添加服务引用)。
  2. 您使用的是System.Net.WebClient吗?切换到新的System.Net.Http.HttpClient - 开始使用其GetStringAsyncGetStreamAsync等异步方法。
  3. 通过这种方式,您可以实际利用异步API的优势,而不必不必阻塞线程。

    async Task Method() {
      var context = GetSomeContext();
      var result = await TimeConsumingWebServiceCallAsync();
      context.Result = result;
    }
    
    async Task TimeConsumingWebServiceCallAsync() {
        var httpClient = new HttpClient();
        var results = await httpClient.GetStringAsync(url); // or await wcfProxy.YourWCFMethodAsync();         
        // do processing if necessary
        return results;
    }
    

答案 1 :(得分:0)

您可以使用静态Task.Run方法:

async void Method() {
  var context = GetSomeContext();
  await Task.Run(() => 
  {
     var result = TimeConsumingWebServiceCall();
     context.Result = result;
  });
}

async void Method() {
  var context = GetSomeContext();
  context.Result = await Task.Run(() => TimeConsumingWebServiceCall());
}

答案 2 :(得分:0)

这是您可以执行的操作,以便包装TimeConsumingWebServiceCall以便可以异步执行

async void MethodAsync()
{
    var context = GetSomeContext();
    context.Result = await Task.Factory.StartNew(() => 
        {
            return TimeConsumingWebServiceCall();
        });
}

更高级的方法

async void Method()
{
    var TimeConsumingTask = Task.Factory.StartNew(() => 
        {
            return TimeConsumingWebServiceCall();
        });
    var context = GetSomeContext();
    context.Result = await TimeConsumingTask;
}

这背后的原因是甚至在您调用TimeConsumingTask之前启动GetSomeContext(),因为它可以与TimeConsumingTask平行执行。
GetSomeContext()完成await后,TimeConsumingTask完成分配结果。{/ p>