2010-10-27 226 views
0

雖然試圖通過BeginSend調用爲隊列發送消息,但看起來像是阻塞調用。C#Socket.BeginSend有時似乎同步行爲?

Specificly我:

public void Send(MyMessage message) 
{ 
    lock(SEND_LOCK){ 
    var state = ... 
    try { 
     log.Info("Begin Sending..."); 
     socket.BeginSend(message.AsBytes(),0, message.ByteLength, SocketFlags.None, 
     (r) => EndSend(r), state); 
     log.Info("Begin Send Complete."); 
    } 
    catch (SocketException e) { 
     ... 
    } 
    } 
} 

的回調將是這樣的:

private void EndSend(IAsyncResult result) { 
    log.Info("EndSend: Ending send."); 
    var state = (MySendState) result.AsyncState; 
    ... 

    state.Socket.EndSend(result, out code); 

    log.Info("EndSend: Send ended."); 

    WaitUntilNewMessageInQueue(); 
    SendNextMessage(); 
    } 

大部分能正常工作的時間,但有時它掛起。記錄表明當BeginSend和EndSend在相同的線程上執行時會發生這種情況。 WaitUntilNewMessageInQueue阻塞,直到隊列中有新消息,所以當沒有新消息時,它可以等待退出一段時間。

至於我可以告訴這個真的不應該是一個問題,但在某些情況下BeginSend導致死鎖情況EndSend被阻塞WaitUntilNewMessageInQueue(預期)塊,但發送被阻塞BeginSend作爲回報,因爲它似乎正在等待EndSend callback te return(not expected)。

這種行爲並不是我期待的。爲什麼BeginSend有時會阻止回調沒有及時返回?

+0

你試過我的建議? – jgauffin 2010-10-28 12:30:51

+0

是的,雖然我還沒有完成。允許異常消除需要重構整個設計。刪除鎖定會導致部分單元測試失敗,但我懷疑這些是可恢復的未處理的異常,但需要進一步調查。 – DefLog 2010-10-29 10:14:45

+0

那麼我的問題解決了,但我仍然不清楚爲什麼我首先發生。我認爲自己非常熟悉線程,但顯然我對套接字的理解是缺乏的,所以我決定花一些時間來研究這個主題。任何refernces將不勝感激。 – DefLog 2010-11-01 10:20:19

回答

2

首先,你爲什麼鎖定Send方法?由於您使用的是BeginSend,鎖定將在發送完成之前釋放。結果是多個發送可以同時執行。其次,不要寫(r) => EndSend(r),只要寫EndSend(不含任何參數)。

Thrid:你不需要在你的狀態中包含套接字。您的EndSend方法與其他任何實例方法一樣工作。因此您可以直接訪問socket字段。

至於你的僵局,很難說。你的委託可能與它有關(編譯器/運行器的優化)。但我對這方面不瞭解。

需要更多幫助?發佈更多代碼。但我建議你解決上述問題(全部四個),然後再試一次。

+0

我不知道爲什麼這已被投票,所提出的觀點都是有效的。 – 2010-10-27 10:50:48

+0

如果你失望,請善待和激勵。 – jgauffin 2010-10-27 11:14:28

+0

對發送的鎖定不是爲了防止同時發送,而是防止在套接字連接或斷開連接時開始發送。將套接字添加到狀態的目的是爲了防止由於斷開連接或配置更改而導致套接字被新實例替換的錯誤。 – DefLog 2010-10-27 11:45:39

1

你在哪個操作系統上運行?

您確定您看到您認爲您所看到的內容嗎?

關於MSDN page的註釋說Send()如果沒有OS緩衝區空間來啓動異步發送,除非您已將套接字置於非阻塞模式,否則可能會阻塞。情況會是這樣嗎?您是否可能非常快速地發送數據並將TCP窗口填充到對等方?如果你進入調試器,調用堆棧顯示什麼?

其餘的猜測是基於我對所涉及的基礎本地技術的理解......

Send()的注意事項可能與I/O被取消有關,如果線程退出,這幾乎肯定取決於底層操作系統,因爲它是低級IO完成端口/重疊I/O問題,隨Windows Vista(看到這裏:http://www.lenholgate.com/blog/2008/02/major-vista-overlapped-io-change.html),並認爲他們是錯誤的,那麼他們可能是錯誤的如何完成(調用EndSend()分配在以後的操作系統)。從Vista開始,如果.Net套接字封裝器在套接字上啓用了正確的選項(請參閱here,我在此討論FILE_SKIP_COMPLETION_PORT_ON_SUCCESS),那麼可能會在發佈線程上調度完成...但是,如果是這種情況,那麼它就是很可能你會看到這種行爲很多,因爲大多數發送可能會「順利」完成,所以你會看到大多數完成發生在同一個線程上 - 我敢肯定,情況並非如此。淨沒有啓用這個選項沒有問...

+0

當我明確將阻塞設置爲false後,問題就顯現出來了。我仍然對這是爲什麼有點模糊。 'http://msdn.microsoft.com/en-us/library/bew39x2a.aspx'沒有提到阻塞模式,我的測試只使用一次發送和接收,所以垃圾郵件並不是真正的問題。 – DefLog 2010-10-27 13:58:55

+0

我改變了阻塞屬性,它似乎工作,但msdn注意到「阻塞屬性對異步方法沒有影響。」在http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.blocking.aspx,所以我仍然困惑。 – DefLog 2010-10-27 14:27:56

+0

單次發送有多大。當你改變阻塞模式時,阻塞的發送是否實際發送數據?我會假設,如果它返回而不是阻塞,那麼它就等於返回E_WOULDBLOCK,這意味着你需要稍後重試操作......儘管如此,所有這些都很奇怪。 – 2010-10-27 16:30:37