給定下面的簡單套接字客戶端類,將其連接到TCP服務器(我使用SocketTest3,可以在線免費獲得)。然後斷開服務器並等待一會兒。你應該得到一個LockRecursionException
。ReaderWriterLockSlim使用Socket.BeginReceive拋出LockRecursionException
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SocketRwlTest
{
public class SocketRwlTest
{
private Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
private readonly ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
private const int maxLength = 200;
public SocketRwlTest(IPAddress address, ushort port)
{
client.Connect(new IPEndPoint(address, port));
ReceiveOne();
}
private void ReceiveOne()
{
rwl.EnterReadLock();
try
{
var inArray = new byte[maxLength];
client.BeginReceive(inArray, 0, maxLength, 0,
new AsyncCallback(ReceivedCallback),
inArray);
}
finally
{
rwl.ExitReadLock();
}
}
private void ReceivedCallback(IAsyncResult ar)
{
client.EndReceive(ar);
ReceiveOne();
}
}
}
我不明白爲什麼它發生在給出的簡化示例中。我知道,只要我收到一個零長度的消息,我就應該停止呼叫ReceiveOne
,但這更多的是練習。我想知道如果一個類似的bug能夠保持在後臺運行的不斷的回調流,並且在沒有明顯不好的事情發生的情況下竊取資源。我必須承認我並不期待這個特別的例外。
問題1:爲什麼會發生這種情況? BeginXYZ
方法可能允許立即在同一個線程上執行回調?如果是這樣的話,誰會說在正常的運行時期間不會發生這種情況?
問題2:在這種情況下是否有避免獲取此異常的方法,同時仍保持「期望」行爲?我的意思是發起一個不間斷的回調流。
我使用Visual Studio 2010和.NET 4
我想你錯過了一個'客戶端的調用。EndReceive'在你的'ReceiveCallback'方法中(雖然這不能回答這個問題) –
The [reference source](http://referencesource.microsoft.com/#System/net/System/Net/Sockets/Socket.cs, 12174aa527fd9499)肯定表明BeginReceive被設計爲可能在單個線程上執行:「我們啓動接收,如果它同步完成,我們會調用回調函數,否則我們將返回一個IASyncResult,調用者可以使用它來等待根據需要打開或檢索最終狀態。「我沒有在文檔或源代碼中看到任何讓我認爲BeginReceive被* required *在另一個線程上調用回調的源代碼。 –
我沒有意識到這一點。實際上,它非常有意義。性能明智的。所以這基本上意味着當回調調用BeginReceive時,您不能使用非遞歸鎖。無論如何,這是一種遞歸,所以它是有道理的。 :) –