Учитывая простой класс клиента сокета ниже, подключите его к TCP-серверу (я использую SocketTest3, свободно доступный в Интернете). Затем отключите сервер и подождите немного. Вы должны получить LockRecursionException
.ReaderWriterLockSlim throws LockRecursionException с Socket.BeginReceive
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
, как только получаю сообщение с нулевой длиной, но это больше упражнение. Я задавался вопросом, может ли подобная ошибка поддерживать постоянный поток обратных вызовов, работающих в фоновом режиме и краду ресурсов без явно плохих событий. Должен признаться, я не ожидал этого исключения.
Вопрос 1: Почему это происходит? Возможно, методы BeginXYZ
позволяют выполнять обратные вызовы мгновенно, в том же потоке? Если это так, кто скажет, что этого не может быть во время нормальной работы?
Вопрос 2: Существуют ли способы избежать этого исключения, сохраняя при этом «желаемое» поведение в этом случае? Я имею в виду огонь без прерывания потока обратных вызовов.
Я использую Visual Studio 2010 с .NET 4.
Я думаю, что вам не хватает вызова для клиента.EndReceive' в методе 'ReceiveCallback' (хотя это не отвечает на вопрос) –
[источник ссылки] (http://referencesource.microsoft.com/#System/net/System/Net/Sockets/Socket.cs, 12174aa527fd9499), безусловно, указывает на то, что BeginReceive предназначен для выполнения в одном потоке: «Мы запускаем прием, и если он завершает синхронно, мы будем вызывать обратный вызов. В противном случае мы вернем IASyncResult, который вызывающий может использовать для ожидания или при необходимости получить окончательный статус ». Я ничего не вижу в документах или исходном коде, которые заставили бы меня подумать, что BeginReceive * требуется * для вызова обратного вызова в другом потоке. –
Я не знал об этом. На самом деле это имеет смысл. С точки зрения производительности. Таким образом, это означает, что вы не можете использовать нерекурсивные блокировки, когда обратный вызов вызывает BeginReceive. Какая-то рекурсия, так что это имеет смысл. :) –