11

着信 TcpClient をリッスンし、着信時にデータを処理するプログラムを作成しています。Listen()メソッドはコンポーネント内の別のスレッドで実行されるため、スレッドセーフである必要があります。ステートメント内でループbreakから抜けた場合、ロックは解除されますか? そうでない場合、どうすればこれを達成できますか?do whilelock()

ありがとう!

(非同期 TCP ソケットに関するその他のアドバイスも歓迎します。)

private void Listen()
{
    do
    {
        lock (_clientLock)
        {
            if (!_client.Connected) break;
            lock (_stateLock)
            {
                if (!_listening) break;
                if (_client.GetStream().DataAvailable) HandleData();
            }
        }
        Thread.Sleep(0);
    } while (true);
}
4

5 に答える 5

21

はい。lock ステートメントは、try/finally 句に変換されます。たとえば、C# 4 では、lock ステートメントは次のようになります。

lock(obj)
{
    // body
}

大まかに翻訳すると (ここの Eric Lippert のブログから引用)、次のようになります。

bool lockWasTaken = false;
var temp = obj;
try 
{ 
    Monitor.Enter(temp, ref lockWasTaken); 
    { 
       // body 
    }
}
finally 
{ 
    if (lockWasTaken) 
        Monitor.Exit(temp); 
}

実行が のスコープを離れるとlock {}、基礎となるロックが自動的に解放されます。これは、Monitor.Exit への呼び出しが try/finally の finally ブロック内で内部的にラップされるため、スコープを終了する方法 (break/return/etc) に関係なく発生します。

于 2010-05-17T20:18:34.910 に答える
3

はい、ロックが解除されます。ILDASM または Reflector を使用して、実際に生成されたコードを確認できます。lock ステートメントは、次のコードの省略形です (大まかに)。

Monitor.Enter(_client);
try
{
  // do your stuff

}
finally {
  Monitor.Exit(_client);
}

finally ブロックが常に実行されることに注意してください。

于 2010-05-17T20:20:45.767 に答える
1

を終了するlock{}と、ロックしたものがロック解除されます (その点では、using ステートメントと同じです)。どこから出るか (最初、最後、または途中) は問題ではなく、ロックの範囲をまったく離れたということです。途中で例外を発生させたらどうなるか考えてみてください。

于 2010-05-17T20:17:15.647 に答える
1

あなたが他のアドバイスを求めたので...私はあなたがロックをネストしていることに気付きました. これ自体は、必ずしも悪いことではありません。しかし、それは私が気をつけている危険信号の 1 つです。コードの別の部分でこれら 2 つのロックを異なる順序で取得すると、デッドロックが発生する可能性があります。あなたのコードに何か問題があると言っているのではありません。間違えやすいので注意が必要です。

于 2010-05-17T20:37:07.450 に答える
0

あなたの質問の残りの半分に答えるには:

非同期 TCP ソケットに関するその他のアドバイスも歓迎します。

簡単に言えば、元の投稿で示されている方法でこれを管理することはできません。代わりに、System.Net.Sockets.TcpClient および System.Net.Sockets.TcpListener クラスに助けを求めてください。BeginAcceptSocket(...) や BeginRead(...) などの非同期呼び出しを使用して、ThreadPool がその仕事を行えるようにします。このように組み立てるのは実に簡単です。

「新しいスレッド」という恐ろしい言葉をコーディングすることなく、必要なすべてのサーバーの動作を実現できるはずです:)

以下は、グレースフル シャットダウン、例外処理などのアイデアを除いた、アイデアの基本的な例です。

public static void Main()
{
    TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, 8080));
    listener.Start();
    listener.BeginAcceptTcpClient(OnConnect, listener);

    Console.WriteLine("Press any key to quit...");
    Console.ReadKey();
}

static void OnConnect(IAsyncResult ar)
{
    TcpListener listener = (TcpListener)ar.AsyncState;
    new TcpReader(listener.EndAcceptTcpClient(ar));
    listener.BeginAcceptTcpClient(OnConnect, listener);
}

class TcpReader
{
    string respose = "HTTP 1.1 200\r\nContent-Length:12\r\n\r\nHello World!";
    TcpClient client;
    NetworkStream socket;
    byte[] buffer;

    public TcpReader(TcpClient client)
    {
        this.client = client;
        socket = client.GetStream();

        buffer = new byte[1024];
        socket.BeginRead(buffer, 0, 1024, OnRead, socket);
    }

    void OnRead(IAsyncResult ar)
    {
        int nBytes = socket.EndRead(ar);
        if (nBytes > 0)
        {
            //you have data... do something with it, http example
            socket.BeginWrite(
                Encoding.ASCII.GetBytes(respose), 0, respose.Length, null, null);

            socket.BeginRead(buffer, 0, 1024, OnRead, socket);
        }
        else
            socket.Close();
    }
}

これを行う方法のより複雑な例については、私が少し前に書いたSslTunnel ライブラリを参照してください。

于 2010-05-17T23:31:34.607 に答える