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長さ 0 のメッセージを受信したらすぐに通話を停止する必要があることはわかっていますが、これは練習問題です。同様のバグが、バックグラウンドで実行されているコールバックの一定のストリームを維持し、明らかに悪いことが起こることなくリソースを盗むことができるかどうか疑問に思いました. この特定の例外を予期していなかったことを認めなければなりません。

質問 1: なぜこれが起こるのですか? BeginXYZ同じスレッドで、メソッドがコールバックを即座に実行できる可能性がありますか? もしそうなら、これが通常の実行時に起こり得ないと誰が言えるでしょうか?

質問 2: この場合、「望ましい」動作を維持しながら、この例外を回避する方法はありますか? つまり、コールバックのノンストップ ストリームを起動するということです。

.NET 4 で Visual Studio 2010 を使用しています。

4

1 に答える 1

1

質問 1: なぜこれが起こるのですか? BeginXYZ メソッドは、同じスレッドでコールバックを即座に実行できるのでしょうか? もしそうなら、これが通常の実行時に起こり得ないと誰が言えるでしょうか?

コメントで mike z が説明したように、このBeginReceive()メソッドは非同期で実行する必要はありません。データが利用可能な場合、同期的実行され、同じスレッドでコールバック デリゲートが呼び出されます。これは定義上、再帰呼び出しであるため、非再帰ロック オブジェクト (ReaderWriterLockSlimここで使用している など)の使用とは互換性がありません。

これは確かに「通常の実行時に」発生する可能性があります。あなたの質問の2番目の部分を理解しているかどうかわかりません。そんなことあり得ないと誰が言える?誰も。それ起こる可能性があります。

質問 2: この場合、「望ましい」動作を維持しながら、この例外を回避する方法はありますか? つまり、コールバックのノンストップ ストリームを起動するということです。

残念ながら、「コールバックのノンストップ ストリームを起動する」という意味もわかりません。

明らかな回避策の 1 つは、オブジェクトのコンストラクターReaderWriterLockSlimに渡すことによって、オブジェクトの再帰を有効にすることです。または、ロックを取得する前にプロパティLockRecursionPolicy.SupportsRecursionを確認することもできます。IsReadLockHeld

あなたのコード例からは、ロックがある理由がまったくわかりません。その特定の方法で使用される理由は気にしないでください。を呼び出す間、ロックをまったく保持しないことが正しい解決策である可能性がありますBeginReceive()。からの結果を処理している間だけ使用してくださいEndReceive()

于 2016-01-22T00:16:58.933 に答える