2014-03-28 107 views
4

我有一種服務與Internet上的第三方進行對話,如果我想要擴展,我不希望我的線程被阻止等待響應正在處理新的請求。解決這個問題的一個方法是使用httpRequest.BeginGetResponse方法並傳遞迴調。與此問題是這種方法立即返回,我沒有什麼可以回報給我的服務的調用者。一旦進入回調,我無法做任何有用的事情。如何在沒有回調的情況下異步發出Web請求

我想什麼是這樣的

public string CallServiceNonBlocking(string url) { 

    var httpRequest = (HttpWebRequest)WebRequest.Create(url); 

    HttpResponse res = httpRequest.FetchNonBlocking(); //this does the work but my thread should be returned to the asp.net thread pool to do other work rather than blocking here 

    return res.GetValue(); 

} 

注意我沒有.NET 4.5提供給我唯一的.NET 4,但作爲參考,我可以看到這兩個版本的解決方案?

+0

您的CallServiceNonBlocking必須在完成抓取時執行完成處理程序/回調。您*可以*使用.NET 4.0中的TPL。 – vcsjones

+0

你能告訴我這看起來好嗎? –

+0

您可能想查看:http://msdn.microsoft.com/en-us/library/dd997423(v=vs.100).aspx – PCG

回答

2

使用.NET 4.5和C#5.0對此進行編碼async/awaitvery easy

如果必須,也可以使其與.NET 4.0/C#4.0異步工作。您需要從AsyncController派生您的控制器並使用AsyncManager。基本步驟在"Using an Asynchronous Controller in ASP.NET MVC"中描述。

鑑於此,你的代碼可能看起來像這樣(未經):

static public Task<WebResponse> GetResponseTapAsync(this WebRequest request) 
{ 
    return Task.Factory.FromAsync(
     (asyncCallback, state) => 
      request.BeginGetResponse(asyncCallback, state), 
     (asyncResult) => 
      request.EndGetResponse(asyncResult), null); 
} 

// ... 

public class YourController : AsyncController 
{ 
    public void YourMethodAsyc(string url) 
    { 
     AsyncManager.OutstandingOperations.Increment(); 

     var request = (HttpWebRequest)WebRequest.Create(url); 
     request.GetResponseTapAsync().ContinueWith(responseTask => 
     { 
      try 
      { 
       var stream = responseTask.Result.GetResponseStream(); 
       using (var streamReader = new StreamReader(stream)) 
       { 
        // still blocking here, see notes below 
        var data = streamReader.ReadToEnd(); 

        AsyncManager.Parameters["data"] = data; 
       } 
      } 
      finally 
      { 
       AsyncManager.OutstandingOperations.Decrement(); 
      } 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    public ActionResult YourMethodCompleted(string data) 
    { 
     return View("Data", new ViewModel 
     { 
      Data = data 
     }); 
    } 
} 

你可以把它進一步貫徹ReadToEndAsync(因爲它是在.NET 4.0中不存在),但你不會能夠使用using。查看this瞭解更多詳情。理想情況下,如果您需要使用.NET 4.0針對ASP.NET MVC,但使用C#5.0中的VS2012 +進行開發,則仍然可以使用async/await,Microsoft爲此提供了Microsoft.Bcl.Async庫。然後你的代碼可能看起來像這樣:

public class YourController : AsyncController 
{ 
    async Task<string> YourMethodAsyncImpl(string url) 
    { 
     var request = (HttpWebRequest)WebRequest.Create(url); 
     using (var response = await request.GetResponseAsync() 
     using (var streamReader = new StreamReader(response.GetResponseStream()) 
      return await streamReader.ReadToEndAsync(); 
    } 

    public void YourMethodAsyc(string url) 
    { 
     AsyncManager.OutstandingOperations.Increment(); 

     YourMethodAsyncImpl(url).ContinueWith(resultTask => 
     { 
      try 
      { 
       AsyncManager.Parameters["data"] = resultTask.Result; 
      } 
      finally 
      { 
       AsyncManager.OutstandingOperations.Decrement(); 
      } 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    public ActionResult YourMethodCompleted(string data) 
    { 
     return View("Data", new ViewModel 
     { 
      Data = data 
     }); 
    } 
} 
0

我會使用Microsoft的Reactive Framework。 (NuGet「Rx-Main」)

它非常容易。

定義你的函數是這樣的:

public IObservable<string> CallServiceNonBlocking(string url) 
{ 
    return Observable.Start(() => 
    { 
     var httpRequest = (HttpWebRequest)WebRequest.Create(url); 
     HttpResponse res = httpRequest.FetchBlocking(); 
     return res.GetValue(); 
    }); 
} 

然後調用它像這樣:

CallServiceNonBlocking(url) 
    .Subscribe(response => 
    { 
     /* handle response here */ 
    }); 

它易於使用ObserveOn使UI線程,或當前上下文的訂閱運行,等等。

+1

我不認爲這會幫助OP的Web服務擴展更好,因爲它仍然阻塞至少一個用於'FetchBlocking'的池線程。 – Noseratio

相關問題