2015-10-14 96 views
2

我有一個多線程的網絡應用程序使用UdpClient,TcpClient,TcpListener,並處理接收的連接和接收數據使用例如。 BeginReceive()EndReceive()回調模式。我是否需要異步同步TCP/UDP客戶端BeginReceive回調

使用UdpClient爲例,在該模式中工作的一般流程我現在用的就是:

  1. 呼叫UdpClient.BeginReceive()
  2. 接收到一個數據包時,將執行接收回調。
  3. 致電UdpClient.EndReceive()收集數據報。
  4. 再次致電UdpClient.BeginReceive()準備接收另一個數據報。 (3)處理收到的數據報(
  5. )。
  6. 重複2 - 5爲多個的數據報被接收

問:既然僅存在單個UdpClient對象,並且由於下一個BeginReceive()之前總是調用EndReceive()圖案,是否需要鎖定/爲這些調用同步訪問對象UdpClient

在我看來,另一個線程不可能干擾這個工作流程或使這些調用不是原子的。 TcpClient.BeginReceive()TcpListener.BeginAcceptTcpClient()的模式非常相似。

獎金問:UdpClient對象需要申報static(和staticobject如果需要)?

注意:我是而不是詢問是否需要執行任何鎖定操作。數據報處理。僅針對這種模式和對象。


EDIT

作爲說明,(忽略異常處理)是此代碼:

private void InitUDP() 
{ 
    udpclient = new UdpClient(new IPEndPoint(IPAddress.Any, Settings.Port)); 

    udpclient.BeginReceive(new AsyncCallback(receiveCallback), udpclient); 
} 

private void receiveCallback(IAsyncResult ar) 
{ 
    UdpClient client = (UdpClient)ar.AsyncState; 

    IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); 

    byte[] datagram = client.EndReceive(ar, ref ep); 

    udpclient.BeginReceive(new AsyncCallback(receiveCallback), udpclient); 

    processDatagram(); 
} 

比這個代碼實際上不同或更少的保護:

private void InitUDP() 
{ 
    udpclient = new UdpClient(new IPEndPoint(IPAddress.Any, Settings.Port)); 

    udpclient.BeginReceive(new AsyncCallback(receiveCallback), udpclient); 
} 

private void receiveCallback(IAsyncResult ar) 
{ 
    UdpClient client = (UdpClient)ar.AsyncState; 

    IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); 

    lock(_lock) 
    { 
     byte[] datagram = client.EndReceive(ar, ref ep); 

     udpclient.BeginReceive(new AsyncCallback(receiveCallback), udpclient); 
    } 

    processDatagram(); 
} 
+0

'UdpClient'成員函數不是線程安全的,所以如果多個線程訪問同一個實例,那麼你有問題。我不確定這是否是你的問題。 – MicroVirus

+0

雖然這是個問題 - 如果在下一個'BeginReceive()'之前始終調用EndReceive()',是否有可能通過這些回調訪問同一個實例?我需要鎖定這些電話嗎? – khargoosh

+0

你是控制線程的人,所以你決定什麼線程訪問什麼。那有可能嗎?是的,如果回調可以合法地訪問實例,例如當它是公開的或者回調是同一實例的成員時 - 以某種方式,如果您可以編寫代碼來訪問回調中的實例並編譯它,是的,你可以訪問它。但是,除非我誤解,否則這不是您真正想要回答的問題,因爲像我說的那樣,您決定每個回調/線程的功能,對嗎? – MicroVirus

回答

3

是否有必要爲這些調用鎖定/同步acccess到UdpClient對象?

不,不完全是,但也許不是你可能會想的原因。

如果您在完成當前數據報的處理之前致電BeginReceiveFrom()(或只是BeginReceive()),那麼實際上可能會同時調用相同的回調。不管這實際上發生取決於很多東西,包括線程調度,多線程IOCP如何現有的線程池,當然是否甚至還有一個數據包接收。

所以你一定有你做處理當前數據報之前,會出現接收新的數據報的風險和處理做到這一點的第一個的處理之前將開始。

現在,如果數據報的處理涉及訪問某些其他共享數據,那麼您肯定需要圍繞其他共享數據進行同步,以確保其他數據的安全訪問。

但是,就數據報本身而言,網絡對象是線程安全的,因爲您不會同時使用它來破壞對象,它仍然由您來確保以一致的方式使用它們。但特別是對於UDP協議,這比TCP更容易。

UDP是不可靠的。它具有缺乏三個非常重要的保證:

  1. 沒有保證數據報將交付。
  2. 無法保證數據報不會多次傳送。
  3. 無法保證數據報將以相對於其發送的其他數據報的順序傳送。

最後一點特別與此有關。您的代碼已經需要能夠處理亂序數據報的處理。因此,無論是由於網絡本身而導致數據報出現亂序,還是因爲您在完成當前處理之前開始了新的I/O操作,您的代碼如果寫入正確,將會成功處理它。


TCP,事情是不同的。如果您已經開始了I/O操作,那麼您再次遇到同樣的問題,那麼在完成當前I/O操作之前,肯定可以完成該操作。但是與UDP不同的是,TCP確實有一些保證,包括在套接字上收到的數據將按照它發送的相同順序接收。

所以只要你不叫BeginReceive(),直到你完全處理完目前已完成的I/O操作,一切都很好。您的代碼以正確的順序查看數據。但是如果你早些時候調用BeginReceive(),那麼當前的線程在處理完當前I/O操作之前可能會被搶佔,而另一個線程可能會處理新完成的I/O操作。

除非您已經對接收到的數據進行了某種同步或排序,以說明無序處理I/O完成的可能性,否則這會損壞您的數據。不好。

有發出多個接收操作的同時很好的理由。但他們通常需要一個高度可擴展的服務器。與發出多個併發接收操作相關的消極因素還有,包括確保數據按正確順序處理的複雜性,以及堆中有多個固定/固定緩衝區的開銷(儘管可以通過多種方式,比如分配足夠大的緩衝區以確保它們處於大對象堆中)。

我會避免以這種方式實現代碼,除非你有一個特定的性能問題,你必須解決。即使在處理UDP時,特別是在處理TCP時。如果你用這種方式來實現代碼,那麼要非常小心。

是否需要將單個UdpClient對象聲明爲靜態(如果需要,還需要靜態鎖定對象)?

您在哪裏存儲對您的UdpClient對象的引用並不重要。如果您的代碼需要同時維護多個UdpClient,則將引用存儲在單個UdpClient類型的字段中將不會很方便。

所有的東西static做的是改變如何訪問該成員。如果不是static,則需要指定找到該成員的實例引用;如果是static,則只需指定類型即可。就這樣。它本身沒有任何與線程安全有關的事情。


最後,關於你的兩個代碼示例,它們在功能上是等價的。沒有必要保護對EndReceive()BeginReceive()的呼叫,並且您的lock不包含這些方法的任何其他部分(例如數據報的實際處理),所以它並不真正完成任何事情(除了可能增加開銷上下文切換)。

在併發場景中,有可能第一個線程在離開lock之前被預佔,但在調用BeginReceive()之後。這可能會導致第二個線程被喚醒來處理第二個I/O完成的回調。然後第二個線程會打到lock並停止,從而允許第一個線程繼續執行並離開lock。但同步所做的一切就是放慢速度。它並不妨礙數據報數據本身的任何併發訪問,這是(可能)很重要的部分。

+0

謝謝@Peter,非常全面的回答和討論。這給我帶來了很多好處。如果我正確理解你的觀點,除非我的性能要求很高,否則對於所有協議,似乎建議在「EndReceive()」調用之後和後續的「BeginReceive()」調用之前移動'processDatagram()'。對於我的特殊應用,這將會很好。至於數據報處理本身,我目前正在排隊並鎖定隊列,這在別處處理。 – khargoosh

+0

澄清:至於數據報處理本身,我目前正在對這些數據進行排隊,並鎖定隊列進行讀取和寫入訪問,並在別處處理。數據本身是共享的並且是線程安全的。 – khargoosh

+0

是的,如果您沒有具體的,觀察到的,可量化的性能問題需要解決,我會確保您在調用'processDatagram()'之前先執行'BeginReceive()'命令。這將使事情變得簡單。 –