ReaderWriterLockSlim 抛出 LockRecursionException Socket.BeginReceive

ReaderWriterLockSlim throws LockRecursionException with Socket.BeginReceive

给定下面的简单套接字客户端 class,将其连接到 TCP 服务器(我使用 SocketTest3,可在线免费获得)。然后断开服务器并稍等片刻。你应该得到一个 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,但这更像是一个练习。我想知道类似的错误是否可以在后台保持持续的回调 运行 流并窃取资源而不会发生明显的坏事。我必须承认我没有预料到 这个 特殊例外。

问题一:为什么会出现这种情况? BeginXYZ 方法是否允许在同一线程上立即执行回调?如果是这样,谁能说在正常运行时不会发生这种情况?

问题 2:在这种情况下,有没有办法在保持 "desired" 行为的同时避免出现此异常?我的意思是触发一个不间断的回调流。

我正在使用 Visual Studio 2010 和 .NET 4。

Question 1: Why does this happen? Are BeginXYZ methods perhaps allowed to execute callbacks instantly, on the same thread? If that's the case, who's to say this couldn't happen during normal runtime?

所述,BeginReceive() 方法 不需要 异步执行。如果数据可用,它 同步执行,在同一个线程中调用回调委托。根据定义,这是一个递归调用,因此与 non-recursive 锁定对象的使用不兼容(例如您在此处使用的 ReaderWriterLockSlim ).

这肯定会发生"during normal runtime"。我不确定我是否理解你问题的第二部分。谁能说这不可能发生?没有人。 可以发生。

Question 2: Are there ways to avoid getting this exception while still maintaining the "desired" behaviour in this case? I mean fire a non-stopping stream of callbacks.

恐怕我也不知道你说的"fire a non-stopping stream of callbacks"是什么意思。

一个明显的解决方法是通过将 LockRecursionPolicy.SupportsRecursion 传递给其构造函数来启用对 ReaderWriterLockSlim 对象的递归。或者,您可以在尝试获取锁之前检查 IsReadLockHeld 属性。

从您的代码示例中并不清楚为什么您拥有锁,更不用说为什么以这种特定方式使用它。正确的解决方案可能是在调用 BeginReceive() 时根本不持有锁。仅在处理 EndReceive().

的结果时使用它