1

给定下面的简单套接字客户端类,将其连接到 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在收到零长度消息后立即停止呼叫,但这更像是一种练习。我想知道类似的错误是否可以保持在后台运行的持续回调流并窃取资源而不会发生明显的坏事。我必须承认我没有预料到这个特殊的例外。

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

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

我正在使用带有 .NET 4 的 Visual Studio 2010。

4

1 回答 1

1

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

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

这当然可以“在正常运行时”发生。我不确定我是否理解你问题的第二部分。谁说这不可能发生?没有人。它可能发生。

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

恐怕我也不知道“触发不间断的回调流”是什么意思。

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

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

于 2016-01-22T00:16:58.933 回答