2017-08-15 119 views
-1

我正在玩SocketAsyncEventArgs和IO完成端口。 我一直在尋找,但我似乎無法找到.NET如何處理競爭條件。.NET如何處理IOCP線程安全?

需要澄清這個堆棧溢出問題: https://stackoverflow.com/a/28690948/855421

作爲一個側面說明,不要忘記,你的要求可能已同步完成。也許你正在一個while循環中讀取TCP數據流,一次讀取512個字節。 如果套接字緩衝區中有足夠的數據,則可以立即返回多個ReadAsyncs,而不進行任何線程切換。[重點礦山]

爲了簡單起見。假設一個客戶端是一臺服務器。服務器正在使用IOCP。如果客戶端是一個快速寫入器,但服務器是一個慢速讀取器,IOCP是否意味着內核/底層進程可以發信號通知多個線程?

1 So, socket reads 512 bytes, kernel signals a IOCP thread 
2 Server processes new bytes 
3 socket receives another X bytes but server is still processing previous buffer

內核是否啓動了另一個線程? SocketAsyncEventArgs有一個Buffer,其定義如下:「獲取數據緩衝區以便與異步套接字方法一起使用。」因此,如果我理解正確,緩衝區在SocketAsyncEventArgs的生存期內不應改變。

什麼阻止SocketAsyncEventArgs.Buffer被IOCP線程2破壞?

還是.NET框架同步IOCP線程?如果是這樣,那麼如果IOCP線程1阻塞先前的讀取,那麼旋轉一個新線程有什麼意義呢?

+0

我相信鏈接的答案指的是連續多次調用ReadAsync的過程,而不是在同一個連接上同時調用多個ReadAsync。如果你這樣做,你不知道訂單數據收到了什麼(雖然我相信你仍然是安全的任何腐敗問題) –

+0

@Damien_The_Unbeliever我相信每個套接字應該有一個ReadAsync。但我讀過的解釋似乎說一個ReadAsync可以旋轉多個IOCP線程。我不明白你爲什麼要這麼做。 –

+0

嗯,我不能說明你讀過哪些內容,尤其是沒有鏈接。你爲什麼試圖深入挖掘?合同是你用特定目標字節數調用'ReadAsync',並且當其他一些數字(或者可能相同數量)的字節被傳送時你會得到通知。如果有任何線程問題需要處理(我不相信有),它們已經被框架處理。 –

回答

0

我一直在尋找,但我似乎無法找到.NET如何處理競爭條件。

大部分情況並非如此。這取決於你。但是,從你的問題來看,你確實存在競爭狀況問題並不清楚。

你問這個文本,在the other answer

如果套接字緩衝區中有足夠的數據,多個ReadAsyncs可以立即返回,而不在所有

首先做任何線程切換,要清楚:方法的名稱是ReceiveAsync(),而不是ReadAsync()。其他類,如StreamReaderNetworkStreamReadAsync()方法,這些方法與你的問題有關很少。現在,這澄清…

該報價是關於相對的競賽狀況。該文本的作者警告您,如果您恰好在已準備好讀取數據的套接字上調用ReceiveAsync(),數據將被同步讀取,並且SocketAsyncEventArgs.Completed事件將在以後生成而不是。線程的責任是調用ReceiveAsync()來處理讀取的數據。

所有這些都會發生在單個線程中。在這種情況下不會有任何競爭條件。

現在,讓我們考慮一下你的「快速作家,慢讀者」場景。可能發生的最糟糕的情況是,第一次讀取可能發生在任何線程中,但不會立即完成,但是當事件發生時,作者已經超過了讀者的速度。在這種情況下,由於處理Completed事件的部分可能會再次調用ReceiveAsync(),現在它將同步返回,因此IOCP線程池線程將在對ReceiveAsync()的調用中循環。不需要新線程,因爲當前IOCP線程正在同步完成所有工作。但它確實阻止該線程處理其他 IOCP事件。

所有這一切將不過是說,如果你有一些其他插座的服務器處理,並且還需要調用ReceiveAsync(),框架必須確保有一個在IOCP線程池可用於處理其他線程I/O。但是,這是一個完全不同的套接字,無論如何你都必須使用完全不同的緩衝區。

再次,沒有競爭條件。


現在,所有的說,如果你想獲得真正混淆,它可以使用異步I/O的.NET Socket API中(無論是與BeginReceive()ReceiveAsync()甚至包裝插座在一個NetworkStream和使用ReadAsync())以這樣的方式,你有一個特定的套接字競爭條件。

我毫不猶豫地提到它,因爲沒有證據表明你的問題與你有關,也沒有你真的對這個級別的細節感興趣。添加這個解釋可能會混淆事物。但是,爲了完整性…

在任何給定的時間都可以在套接字上發出多個讀操作。這與雙緩衝或三緩衝視頻顯示有些類似(如果您熟悉該概念)。這個想法是,當新數據進來時你可能仍然在處理讀操作,並且在完成處理當前讀操作之前,已經有一個新的讀操作正在處理該數據,這將是更高效的。

這聽起來不錯,但由於方式的Windows調度線程的實踐,特別是不保證線程調度的一個特定的順序,如果你試圖實現你的代碼,這樣,您所創建的可能性代碼將看到讀取操作不按順序完成。也就是說,如果您連續兩次調用ReceiveAsync()兩次(當然有兩個不同的SocketAsyncEventArgs對象和兩個不同的緩衝區),則可能會先使用第二個緩衝區調用您的Completed事件處理程序。

這不是因爲讀取操作本身完成不按順序。他們不。因此重點在上面的「你的」。問題在於,當處理IO完成的IOCP線程以正確的順序運行時(因爲緩衝區按照您通過多次調用ReceiveAsync()而提供的順序填充),第二個IOCP線程可以運行成爲可運行的第一個線程實際上預定由Windows運行。

這不難處理。您只需確保在發出讀取操作時跟蹤緩衝區序列,以便稍後以正確的順序重新組合緩衝區。所有可用的異步選項都提供了一種機制,讓您可以包含其他用戶狀態數據(例如SocketAsyncEventArgs.UserToken),因此您可以使用它來跟蹤緩衝區的順序。

再次,這並不常見。對於大多數情況下,完全有序的實現,在完成當前讀取操作後,只發出新的讀取操作,就足夠了。如果您擔心讓多緩衝區讀取實現正確無誤,請不要打擾。堅持簡單的方法。

+0

問題中概念/層次的混淆使我懷疑這是一個A/Y問題 - 就像X/Y問題一樣,但即使X與他們最初處理的問題有很大的距離。 –

+0

不知道爲什麼這是downvoted。它或多或少地回答了我的問題。 –

+0

* ..作者超出了讀者的節奏。在這種情況下,由於處理Completed事件的部分可能會再次調用ReceiveAsync(),現在它將同步返回,因此IOCP線程池線程將在對ReceiveAsync()的調用上循環。不需要新線程,因爲當前IOCP線程正在同步完成所有工作。但它確實阻止了該線程處理其他IOCP事件。* - 正是我想知道的 –