繁体   English   中英

如果你突破Lock()语句会发生什么?

[英]What happens if you break out of a Lock() statement?

我正在编写一个程序,它监听传入的TcpClient并在数据到达时处理数据。 Listen()方法在组件内的单独线程上运行,因此它需要是线程安全的。 如果我break了一个的do while循环,而我是一个内lock()语句,将锁被释放? 如果没有,我该如何做到这一点?

谢谢!

(关于异步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);
}

是。 lock语句转换为try / finally子句。 例如,在C#4中,锁定语句如下:

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 {}的范围时,底层锁将自动释放。 无论你如何退出范围(break / return / etc),都会发生这种情况,因为对Monitor.Exit的调用是在try / finally的finally块内部包装的。

是的,锁将被释放。 您可以使用ILDASM或Reflector查看实际生成的代码。 lock语句是以下代码的简写(粗略)。

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

}
finally {
  Monitor.Exit(_client);
}

请注意,始终执行finally块。

退出lock{} ,它将解锁您锁定的内容(就像在这方面的使用声明一样)。 你退出的地方(开头,结尾或中间)并不重要,而是你完全放弃了锁的范围。 想想如果你在中间引发异常会发生什么。

因为你要求其他建议......我注意到你正在筑巢锁。 这本身并不一定是坏事。 但是,这是我注意的一面红旗。 如果您在代码的另一部分中以不同的顺序获取这两个锁,则可能会出现死锁。 我并不是说您的代码有任何问题。 这是值得注意的事情,因为它容易出错。

要回答你问题的另一半:

关于异步TCP套接字主题的任何其他建议也是受欢迎的

简单地说,我不会以您原来的帖子所展示的方式管理这个。 而是从System.Net.Sockets.TcpClient和System.Net.Sockets.TcpListener类中寻求帮助。 使用像BeginAcceptSocket(...)和BeginRead(...)这样的异步调用,并允许ThreadPool完成它的工作。 用这种方式拼凑起来真的很容易。

您应该能够实现所需的所有服务器行为,而无需编写可怕的单词“new Thread”:)

以下是该想法的基本示例,减去正常关闭,异常处理等的想法:

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库

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM