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

发布于 2024-09-02 08:16:29 字数 622 浏览 0 评论 0原文

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

谢谢!

(也欢迎有关异步 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);
}

I'm writing a program which listens to an incoming TcpClient and handles data when it arrives. The Listen() method is run on a separate thread within the component, so it needs to be threadsafe. If I break out of a do while loop while I'm within a lock() statement, will the lock be released? If not, how do I accomplish this?

Thanks!

(Any other advice on the subject of Asynchronous TCP Sockets is welcome as well.)

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

千柳 2024-09-09 08:16:29

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

Yes. The lock statement translates into a try/finally clause. In C# 4, for example, a lock statement like so:

lock(obj)
{
    // body
}

roughly translates (taken from Eric Lippert's blog here) to:

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

When the execution leaves the scope of the lock {}, the underlying lock will be released automatically. This will happen no matter how you exit scope (break/return/etc), since the call to Monitor.Exit is wrapped, internally, inside of the finally block of a try/finally.

○愚か者の日 2024-09-09 08:16:29

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

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

}
finally {
  Monitor.Exit(_client);
}

请注意,finally 块始终会被执行。

Yes, the lock will be released. You can use ILDASM or Reflector to look at the actual generated code. The lock statement is shorthand for the following code (roughly).

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

}
finally {
  Monitor.Exit(_client);
}

Notice the finally block is always executed.

烟花肆意 2024-09-09 08:16:29

因为你寻求其他建议......我注意到你正在嵌套锁。这本身并不一定是一件坏事。但是,这是我要注意的危险信号之一。如果您在代码的另一部分以不同的顺序获取这两个锁,则可能会出现死锁。我并不是说你的代码有什么问题。这只是需要注意的其他事情,因为很容易出错。

Because you asked for other advice...I noticed that you are nesting locks. This, by itself, is not necessarily a bad thing. But, it is one my red flags I watch out for. There is the possibility of a deadlock if you ever acquire those two locks in a different order in another part of your code. I am not saying there is anything wrong with your code. It is just something else to watch out for because it is easy to get wrong.

颜漓半夏 2024-09-09 08:16:29

一旦您退出lock{},它将解锁您已锁定的内容(在这方面,它就像一个 using 语句)。你从哪里退出(开始、结束或中间)并不重要,重要的是你完全离开了锁的范围。想想如果你在中间抛出异常会发生什么。

Once you exit the lock{}, it will unlock what you have locked (it's just like a using statement in that regard). It doesn't matter where you exit (the beginning, the end, or the middle), it's that you left the scope of the lock at all. Think about what would happen if you raised an exception in the middle.

离旧人 2024-09-09 08:16:29

回答你问题的另一半:

也欢迎有关异步 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 库 我不久前写的。

To answer the other half of your question:

Any other advice on the subject of Asynchronous TCP Sockets is welcome as well

Simply put I wouldn't manage this in the fashion demonstrated by your original post. Rather seek help from the System.Net.Sockets.TcpClient and the System.Net.Sockets.TcpListener classes. Use the async calls like BeginAcceptSocket(...) and BeginRead(...) and allow the ThreadPool to do it's job. It's really pretty easy to put together that way.

You should be able to achieve all the server behavior you desire without ever coding the dreaded words "new Thread" :)

Here is a basic example of the idea, minus the idea of graceful shutdown, exception handling ect:

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();
    }
}

For a much more complicated example of how to do this see the SslTunnel Library I wrote a while ago.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文