2013-01-13 104 views
1

考慮以下代碼:如何使用WaitHandle等待異步調用的完成?

class Program 
     { 
      static void Main(string[] args) 
      { 
       Master master = new Master(); 
       master.Execute(); 
      } 
     } 

    class TestClass 
    { 
     public void Method(string s) 
     { 
      Console.WriteLine(s); 
      Thread.Sleep(5000); 
      Console.WriteLine("End Method()"); 
     } 
    } 
    class Master 
    { 
     private readonly TestClass test = new TestClass(); 

     public void Execute() 
     { 
      Console.WriteLine("Start main thread.."); 
      Action<String> act = test.Method; 
      IAsyncResult res = act.BeginInvoke("Start Method()..", x => 
      { 
       Console.WriteLine("Start Callback.."); 
       act.EndInvoke(x); 
       Console.WriteLine("End Callback"); 
      }, null); 
      Console.WriteLine("End main thread"); 
      Console.ReadLine(); 
     } 
    } 

我們有結果:

Start main thread.. 
End main thread 
Start Method().. 
End Method() 
Start Callback.. 
End Callback 

所以,我想結果是:

Start main thread..  
Start Method().. 
End Method() 
Start Callback.. 
End Callback 
End main thread 

我怎麼能等待async這個代碼?我查MSDN文章"Calling Synchronous Methods Asynchronously",發現這個:

  • 做一些工作,然後調用EndInvoke阻塞,直到通話 完成:

    調用BeginInvoke你可以做以下後。

  • 獲取一個WaitHandle使用IAsyncResultAsyncWaitHandle
    屬性,使用其WaitOne方法,直到
    WaitHandle用信號阻斷執行,然後調用EndInvoke
  • 輪詢IAsyncResult返回BeginInvoke確定何時 異步調用已完成,然後調用EndInvoke
  • 將回調方法的代理傳遞給BeginInvoke。當異步調用 完成時,方法 在ThreadPool線程上執行。回調方法調用EndInvoke

我覺得這個第二個更好的變種。但是怎麼實現呢?特別是我感興趣超載WaitOne()Blocks the current thread until the current WaitHandle receives a signal)。如何正確做到這一點?我的意思是這種情況下的常見模式。

UPDATE:

現在我用Task<T>

class Program 
    { 
     static void Main(string[] args) 
     { 
      Master master = new Master(); 
      master.Execute(); 
     } 
    } 

    class WebService 
    { 
     public int GetResponse(int i) 
     { 
      Random rand = new Random(); 
      i = i + rand.Next(); 
      Console.WriteLine("Start GetResponse()"); 
      Thread.Sleep(3000); 
      Console.WriteLine("End GetResponse()"); 
      return i; 
     } 

     public void SomeMethod(List<int> list) 
     { 
      //Some work with list   
      Console.WriteLine("List.Count = {0}", list.Count); 
     } 
    } 
    class Master 
    { 
     private readonly WebService webService = new WebService(); 

     public void Execute() 
     { 
      Console.WriteLine("Start main thread.."); 
      List<int> listResponse = new List<int>(); 
      for (int i = 0; i < 5; i++) 
      { 
       var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1)) 
        .ContinueWith(x => 
        { 
         Console.WriteLine("Start Callback.."); 
         listResponse.Add(x.Result); 
         Console.WriteLine("End Callback"); 
        }); 
      } 
      webService.SomeMethod(listResponse); 
      Console.WriteLine("End main thread.."); 
      Console.ReadLine(); 
     } 
    } 

主要問題,這是SomeMethod()變成空list

結果: enter image description here

現在我有可怕的解決方案:(

public void Execute() 
     { 
      Console.WriteLine("Start main thread.."); 
      List<int> listResponse = new List<int>(); 
      int count = 0; 
      for (int i = 0; i < 5; i++) 
      { 
       var task = Task<int>.Factory.StartNew(() => webService.GetResponse(1)) 
        .ContinueWith(x => 
        { 
         Console.WriteLine("Start Callback.."); 
         listResponse.Add(x.Result); 
         Console.WriteLine("End Callback"); 
         count++; 
         if (count == 5) 
         { 
          webService.SomeMethod(listResponse); 
         } 
        }); 

      } 
      Console.WriteLine("End main thread.."); 
      Console.ReadLine(); 
     } 

結果:

enter image description here

這就是我需要等待異步調用哪有。我在這裏使用WaitTask

更新2:

class Master 
{ 
    private readonly WebService webService = new WebService(); 
    public delegate int GetResponseDelegate(int i); 

    public void Execute() 
    { 
     Console.WriteLine("Start main thread.."); 
     GetResponseDelegate act = webService.GetResponse; 
     List<int> listRequests = new List<int>(); 
     for (int i = 0; i < 5; i++) 
     { 
      act.BeginInvoke(1, (result => 
      {      
       int req = act.EndInvoke(result); 
       listRequests.Add(req); 
      }), null); 
     } 

     webService.SomeMethod(listRequests); 
     Console.WriteLine("End main thread.."); 
     Console.ReadLine(); 
    } 
} 

回答

1

我認爲這是你想要的。我們正在開始任務,然後等待他們完成,畢竟完成後,我們正在獲得結果。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Master master = new Master(); 
     master.Execute(); 
    } 
} 

class WebService 
{ 
    public int GetResponse(int i) 
    { 
     Random rand = new Random(); 
     i = i + rand.Next(); 
     Console.WriteLine("Start GetResponse()"); 
     Thread.Sleep(3000); 
     Console.WriteLine("End GetResponse()"); 
     return i; 
    } 

    public void SomeMethod(List<int> list) 
    { 
     //Some work with list   
     Console.WriteLine("List.Count = {0}", list.Count); 
    } 
} 
class Master 
{ 
    private readonly WebService webService = new WebService(); 
    public void Execute() 
    { 
     Console.WriteLine("Start main thread.."); 
     var taskList = new List<Task<int>>(); 
     for (int i = 0; i < 5; i++) 
     { 
      Task<int> task = Task.Factory.StartNew(() => webService.GetResponse(1)); 
      taskList.Add(task); 
     } 

     Task<List<int>> continueWhenAll = 
      Task.Factory.ContinueWhenAll(taskList.ToArray(), 
          tasks => tasks.Select(task => task.Result).ToList()); 

     webService.SomeMethod(continueWhenAll.Result); 
     Console.WriteLine("End main thread.."); 
     Console.ReadLine(); 
    } 

} 

與BeginIvoke/EndInvoke會

public void Execute() 
{ 
    Func<int, int> func = webService.GetResponce; 

    var countdownEvent = new CountdownEvent(5); 
    var res = new List<int>(); 
    for (int i = 0; i < 5; ++i) 
    { 
     func.BeginInvoke(1, ar => 
         { 
          var asyncDelegate = (Func<int, int>)((AsyncResult)ar).AsyncDelegate; 
          int ii = asyncDelegate.EndInvoke(ar); 
          res.Add(ii); 
          ((CountdownEvent)((AsyncResult)ar).AsyncState).Signal(); 
         }, countdownEvent); 
    } 
    countdownEvent.Wait(); 
    Console.WriteLine(res.Count); 
} 
+0

感謝您的回覆!看來,這是我需要的! :) – Alexandr

+0

哈姆雷特,你可以請檢查我的更新2後?我對學術目的感興趣,如果使用異步BeginInvoke()是否可以實現與您的答覆中相同的結果? – Alexandr

1

從你的示例代碼,目前尚不清楚爲什麼你需要異步調用。如果你不這樣做,你可以同時撥打test.Method();

假設你需要異步執行,不要打擾陳舊的Delegate.BeginInvoke的東西。使用新的基於Task API:

var task = Task.Factory.StartNew(() => test.Method()); 
的任務

然後,您可以Waitawait或使用Task.ContinueWith(選擇你的情況下,適當的技術)。

+0

USR醜陋的解決方案,感謝反饋!真的,我給了不好的樣本代碼。我使用Windows Phone 7.5和異步請求到Web服務。在示例代碼中,我隱藏了這一刻,因爲我想在示例中使用簡單的代碼。我的帖子更新與現實代碼最爲相似。我使用基於任務的API,它在Windows Phone 7中可用:) – Alexandr

+0

現在,這是一個完全不同的問題......看起來您需要關於併發性如何工作的很好的教程。您的代碼不安全,可能會受益於一些最佳做法。我建議使用教程材料來選擇基礎知識。 – usr

+0

你是對的,你能教一個好的教程嗎? – Alexandr