2015-09-04 87 views
2

我們正在等待函數發生事件。但我認爲該規範是不正確的(它有效,但對我來說它看起來錯了!)。正確的等待方式事件

首先,這是代碼我的同事寫道:

public string Dispatch(Request data) 
    { 
     var uri = ... 
     string _result = null; 
     using (var ws = new WebSocket(uri)) 
     { 
      ws.OnMessage += (sender, e) => 
      { 
       _result = e.Data; 
      }; 

      ws.Send(request); 
      while (_result == null) 
      { 
       Thread.Sleep(10); 
      } 

      return _result; 
     } 
    } 

有沒有意識到這更好的辦法?我想我可以使用AutoResetEvent,但這是否更好?有沒有一種方法可以實現代碼,以便線程可以在等待答案時重用? (我知道如何與TaskCompletitionSource做到這一點,但是這也是正確的同步功能?)

我的想法是:

public string Dispatch(Request data) 
    { 
     var uri = ... 

     using (var ws = new WebSocket(uri)) 
     { 
      TaskCompletionSource<Guid> tcs; 
      ws.OnMessage += (sender, e) => 
      { 
       tcs.SetResult(e.Data); 
      }; 

      ws.Send(request); 

      return tcs.Task.Result; 
     } 
    } 

public string Dispatch(Request data) 
    { 
     var uri = ... 
     string _result = null; 
     var event = new AutoResetEvent(false); 
     using (var ws = new WebSocket(uri)) 
     { 
      TaskCompletionSource<Guid> tcs; 
      ws.OnMessage += (sender, e) => 
      { 
       _result = e.Data; 
       event.Set(); 
      }; 

      ws.Send(request); 

      event.WaitOne(); 
      return _result; 
     } 
    } 
+5

使用TCS進行異步處理。 – i3arnon

+1

_ [我可以在Stack Overflow上發佈關於優化代碼的問題嗎?....不,它不是最好的地方 - 雖然它是關於主題的,但是對於這樣的問題有更好的地方。您可以將代碼複製到Code Review中 - 但請確保閱讀他們的幫助中心,查看他們對一個好問題的期望。](http://meta.stackoverflow.com/questions/261841/can-i-post - 關於優化代碼堆棧溢出)_ – MickyD

+0

我已經這樣做了,但使用代碼的公司不使用異步/等待... –

回答

0

有沒有更好的辦法意識到這一點?

幾乎任何形式的等待都會比原來的代碼更好,每隔10毫秒就會爲了檢查一個標誌而醒來。唯一會更糟的是根本不要撥打Thread.Sleep()。但那隻會更糟糕。

我想我可以使用AutoResetEvent,但是這樣更好嗎?

恕我直言,使用TaskCompletionSourceawait是最好的。這樣做將解決您的下一個問題:

有沒有一種方法來實現代碼,線程可以重用,而它正在等待答案?

I.e.與await,線程實際上被釋放用於其他用途。調用await的方法將返回await語句,允許該線程繼續執行其他代碼。當然,這意味着調用方法需要以某種方式處理異步;通常它的工作方式是async被推回到線程的頂部,例如,在某種事件分派循環中,例如Winforms或WPF中的循環。

你沒有具體關於爲什麼你的公司沒有使用await。如果限制是由於被鎖定到早期版本的.NET中,則可能無法使用TaskCompletionSource,因爲它只能在.NET 4及更高版本中使用。

即使您正在使用.NET 4(或更高版本)並且可以使用TaskCompletionSource,等待該任務也無法解決您對釋放其他用途的線程的擔憂。這可能是也可能不是可尋址的; Task類確實有ContinueWith()方法,該方法允許您在完成Task時明確提供要調用的繼續委託。但是,如果您的Dispatch()方法本身不能用於異步上下文—,即在操作完成之前它不能返回—,那麼除了在操作完成之前,您別無選擇,只能阻止該線程的執行。

(根據—這太少了已經在這裏提供的背景下能夠提供具體的建議—這是可能的,你可以從調度等操作Dispatch()方法,同時它的等待,但是這會顯著複雜化恕我直言,最好升級到.NET 4.5/C#5並使用await ......這可能不是一個長期維護問題,而是試圖破解更現代版本的固有功能框架和語言)。

最後,我會注意到,作爲一般規則,您應該避免使用ManualResetEventAutoResetEvent,因爲它們基於非託管Windows同步對象,並且相當「重量級」。 .NET提供更高效的本機同步機制。至少,您可以使用Monitor類,使用Wait()Pulse()方法;可以使用CountdownEventSemaphoreSlim找到等效機制。每個語義都略有不同,但都可以用來輕鬆實現線程之間的簡單信號。