C# 如果中断Lock()语句,会发生什么?

C# 如果中断Lock()语句,会发生什么?,c#,.net,multithreading,locking,tcpclient,C#,.net,Multithreading,Locking,Tcpclient,我正在编写一个程序,它侦听传入的TcpClient并在它到达时处理数据。Listen()方法在组件内的单独线程上运行,因此它需要是线程安全的。如果我在lock()语句中时从do循环中中断,锁是否会被释放?如果没有,我如何实现这一点 谢谢 (关于异步TCP套接字的任何其他建议也都是受欢迎的。) 一旦您退出锁{},它将解锁您锁定的内容(这就像是一个using语句)。退出位置(开始、结束或中间)无关紧要,重要的是您完全离开了锁的作用域。想想如果你在中间引发异常会发生什么。 是的。lock语句转换为tr

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

谢谢

(关于异步TCP套接字的任何其他建议也都是受欢迎的。)


一旦您退出
锁{}
,它将解锁您锁定的内容(这就像是一个using语句)。退出位置(开始、结束或中间)无关紧要,重要的是您完全离开了锁的作用域。想想如果你在中间引发异常会发生什么。 是的。lock语句转换为try/finally子句。例如,在C#4中,一个lock语句如下:

lock(obj)
{
    // body
}
将()大致转换为:


当执行离开
锁{}
的作用域时,底层锁将自动释放。无论您如何退出作用域(break/return/etc),都会发生这种情况,因为对Monitor.exit的调用在内部包装在try/finally的finally块中。

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


请注意,始终执行finally块。

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

要回答你问题的另一半:

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

简单地说,我不会按照你原来的帖子所展示的方式来处理这个问题。而是从System.Net.Sockets.TcpClient和System.Net.Sockets.TcpListener类寻求帮助。使用像BeginAcceptSocket(…)和BeginRead(…)这样的异步调用,并允许线程池完成它的工作。用这种方式组合起来真的很容易

您应该能够实现所需的所有服务器行为,而无需编写可怕的单词“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();
    }
}

有关如何实现这一点的更为复杂的示例,请参见我不久前编写的。

顺便说一句-这是正确的,Eric Lippert在C#4中记录了这一变化。注意,finally块并不总是执行。仅当控件离开try时才执行finally块。控件可能不会离开try;try块可能包含无限循环。另一个线程可能会使进程失败。管理员可能会终止该进程。一个线程可能会连续两次命中堆栈保护页,从而导致进程停止。有人可能会拔掉机器的插头。在所有这些情况下,finally块都不会执行。@Eric Lippert:您遗漏了改变CPU中电压的宇宙射线:)嗯,鉴于Eric Lippert在StackOverflow上的表现,我的描述必须更加精确。:)你去坐牢,然后你就会被释放。非常重要的是想想如果你在中间引发了异常会发生什么。在一个关键的操作过程中,一些异常和意外的事情出错了,你做的第一件事是什么?释放锁,以便其他代码可以进入并访问在关键操作中间引起异常的资源!在异常情况下释放锁这一事实意味着你有更多的工作要做,而不是更少。这是真的,我不是这么想的。这是一个极好的观点。我的想法是,如果锁没有打开怎么办。除了一个例外,它在堆栈中的什么位置会被捕获是不可预测的。如果有人不小心,异常可能会在没有使用锁的提示的情况下冒泡几个级别。现在有一个异常需要处理,一个锁定的对象正在阻止“潜在的”其他成功操作的发生。问题是,不管你选择什么策略,都可能发生可怕的事情。锁和异常混合得很差。如果我们有廉价的软件事务性内存,问题就不会那么严重;我们可以简单地将内存回滚到进入锁之前的状态。但是我们的工具箱里还没有这个工具。谢谢你的警告!我要确保以相同的顺序锁定对象,tho,这样它就不会死锁。在lock语句中使用GOTO语句从锁中分支时,这也会起同样的作用,对吗?
bool lockWasTaken = false;
var temp = obj;
try 
{ 
    Monitor.Enter(temp, ref lockWasTaken); 
    { 
       // body 
    }
}
finally 
{ 
    if (lockWasTaken) 
        Monitor.Exit(temp); 
}
Monitor.Enter(_client);
try
{
  // do your stuff

}
finally {
  Monitor.Exit(_client);
}
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();
    }
}