2012-07-19 181 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