2012-07-19 143 views
1

我在WCF中有一个Web服务,它使用一些外部Web服务,所以我想要做的是使此服务异步以释放线程,等待所有外部服务,然后将结果返回给客户端。在内部有多个异步调用的异步WCF服务

随着框架4.0

public class MyService : IMyService 
{ 
    public IAsyncResult BeginDoWork(int count, AsyncCallback callback, object serviceState) 
    {  
     var proxyOne = new Gateway.BackendOperation.BackendOperationOneSoapClient(); 
     var proxyTwo = new Gateway.BackendOperationTwo.OperationTwoSoapClient(); 

     var taskOne = Task<int>.Factory.FromAsync(proxyOne.BeginGetNumber, proxyOne.EndGetNumber, 10, serviceState); 
     var taskTwo = Task<int>.Factory.FromAsync(proxyTwo.BeginGetNumber, proxyTwo.EndGetNumber, 10, serviceState); 

     var tasks = new Queue<Task<int>>(); 
     tasks.Enqueue(taskOne); 
     tasks.Enqueue(taskTwo); 

     return Task.Factory.ContinueWhenAll(tasks.ToArray(), innerTasks => 
     { 
      var tcs = new TaskCompletionSource<int>(serviceState); 
      int sum = 0; 

      foreach (var innerTask in innerTasks) 
      { 
       if (innerTask.IsFaulted) 
       { 
        tcs.SetException(innerTask.Exception); 
        callback(tcs.Task); 
        return; 
       } 

       if (innerTask.IsCompleted) 
       { 
        sum = innerTask.Result; 
       } 
      } 

      tcs.SetResult(sum); 

      callback(tcs.Task); 
     }); 
    } 

    public int EndDoWork(IAsyncResult result) 
    { 
     try 
     { 
      return ((Task<int>)result).Result; 
     } 
     catch (AggregateException ex) 
     { 
      throw ex.InnerException; 
     } 

    } 
} 

我的问题,这里有:

  1. 该代码使用三个线程:一个是在 BeginDoWork实例化,另外一个是实例化时的代码输入 里面的匿名方法ContinueWhenAll,当 执行回调时,最后一个是EndDoWork。是正确的还是 我在通话中做错了什么?我应该使用任何 同步上下文吗?注意:主线程上的同步上下文为空 。

  2. 如果我在 线程之间“共享”信息(例如回调函数)会发生什么?这是否会导致 性能问题或匿名方法就像我可以共享数据的封闭?

随着框架4.5和异步和等待

现在有了框架4.5,代码似乎太简单比以前:

public async Task<int> DoWorkAsync(int count) 
    { 
     var proxyOne = new Backend.ServiceOne.ServiceOneClient(); 
     var proxyTwo = new Backend.ServiceTwo.ServiceTwoClient(); 

     var doWorkOne = proxyOne.DoWorkAsync(count); 
     var doWorkTwo = proxyTwo.DoWorkAsync(count); 

     var result = await Task.WhenAll(doWorkOne, doWorkTwo); 

     return doWorkOne.Result + doWorkTwo.Result; 
    } 

但在这种情况下,当我调试应用程序,我总是看到代码在同一个线程上执行。所以我在这里的问题是:

3.当我在等待“awaitable”代码时,该线程是否被释放并返回到线程池以执行更多请求?

3.1。如果是这样,我想当我从await任务中得到一个结果时,执行完成在与之前调用相同的线程上。那可能吗?如果该线程正在处理另一个请求会发生什么?

3.2如果不是,我该如何释放线程以使用Asycn和Await模式将它发送回线程池?

谢谢!

+0

FWIW,使用异步定位包,您可以使用4.0项目的异步/等待。 – 2012-07-19 23:11:16

+0

4.0可以返回任务(即使它不使用异步/等待)?我想,这会简化代码。任务实现IAsyncResult IIRC – 2012-07-19 23:25:14

回答

2

1.  该代码使用三个线程:一个是在BeginDoWork实例化,另外一个当代码匿名方法ContinueWhenAll进入里面就是实例化,并在执行回调的最后一个,在这种情况EndDoWork。这是正确的,或者我在通话中做错了什么?我应该使用任何同步上下文吗?

最好用“任务”而不是“线索”来思考。您在这里确实有三项任务,每项任务都会一次在线程池中运行。

2.  如果我在线程之间“共享”信息,例如回调函数,会发生什么?这会导致性能问题还是匿名方法就像我可以共享数据的关闭?

您不必担心同步,因为这些任务中的每一个都不能同时运行。 BeginDoWork在返回之前注册延续,因此延续可以运行时已经实际完成。在继续完成之前可能不会调用EndDoWork;但即使是这样,它也会阻止直到延续完成。

(技术上,延续可以开始运行BeginDoWork完成之前,但BeginDoWork只是返回在这一点上,所以也没关系)。

3.  当我在等待“awaitable”代码时,该线程是否被释放并返回到线程池以执行更多请求?

是的。

3.1。如果是这样,我想当我从await任务中得到一个结果时,执行完成在与之前调用相同的线程上。那可能吗?如果该线程正在处理另一个请求会发生什么?

否。您的主机(在本例中为ASP.NET)可以在它碰巧可用的任何线程上继续使用async方法。

这是完全安全的,因为只有一个线程正在执行时间。

P.S.我推荐的

var result = await Task.WhenAll(doWorkOne, doWorkTwo); 
return result[0] + result[1]; 

代替

var result = await Task.WhenAll(doWorkOne, doWorkTwo); 
return doWorkOne.Result + doWorkTwo.Result; 

因为Task.Resultasync编程来避免。

+0

对于代码的最后一点,做var的优点/缺点是什么result1 = await doWorkOne; var result2 = await doWorkTwo;然后添加它们?避免'包装'任务,尽管这可能可以忽略不计。 – 2012-07-19 23:17:40

+0

WRT'可以继续任何线程',我认为这是不正确的,由于asp.net同步上下文,而不是使用ConfigureAwait(false);? – 2012-07-19 23:19:37

+0

@James:'await doWorkOne();等待doWorkTwo()方法启动'One',然后直到'One'完成后才启动'Two','WhenAll(doWorkOne(),doWorkTwo())'方法启动它们,然后继续 – 2012-07-20 01:48:18