Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#:从锁定块调用事件_C#_Multithreading_Events_Locking_Deadlock - Fatal编程技术网

C#:从锁定块调用事件

C#:从锁定块调用事件,c#,multithreading,events,locking,deadlock,C#,Multithreading,Events,Locking,Deadlock,我经常听说在调用事件侦听器之前解锁任何锁是一个好主意,以避免死锁。但是,由于lock{}块是由C#中的同一个线程重入的,因此可以从锁定块调用事件,还是需要复制相关状态数据并在锁块外部调用事件 如果没有,请举例说明从lock{}块中调用事件何时会出现问题 谢谢是的,从保持锁中调用事件将被视为不好的设计。主要是因为有一个从锁定部分的转换,如果这是抛出一个错误或有问题,锁不一定会被释放 通常,您希望尽可能减少在锁中的时间,以防止锁持有时间过长(导致延迟),并减少代码在锁持有期间失败的可能性。问题不在于

我经常听说在调用事件侦听器之前解锁任何锁是一个好主意,以避免死锁。但是,由于
lock{}
块是由C#中的同一个线程重入的,因此可以从锁定块调用事件,还是需要复制相关状态数据并在锁块外部调用事件

如果没有,请举例说明从
lock{}
块中调用事件何时会出现问题


谢谢

是的,从保持锁中调用事件将被视为不好的设计。主要是因为有一个从锁定部分的转换,如果这是抛出一个错误或有问题,锁不一定会被释放


通常,您希望尽可能减少在锁中的时间,以防止锁持有时间过长(导致延迟),并减少代码在锁持有期间失败的可能性。

问题不在于事件处理程序可能试图调用您已经拥有的锁,问题是,事件处理程序可能会尝试获取其他锁(可能会阻塞并设置死锁),或者事件处理程序可能会启动一些长时间运行的任务,如数据库查询(在锁完成之前,其他线程无法访问您的锁)。一般的规则是你应该尽可能短的时间持有一把锁


将线程与您无法控制的事件处理程序混合在一起肯定会给您带来麻烦。我当前遇到一些问题,因为我从串行端口的接收线程引发了一个事件。某些事件处理程序代码决定阻塞并等待,直到从串行端口接收到另一条消息。这将是一个漫长的等待,因为您刚刚阻止了一个也是唯一一个接收线程!我甚至不能对任何人发火,因为我写了两段代码(相隔一年,所以我有时间忘记细节)。

我不记得曾经有过从
语句中引发事件的需要。但不难想象事情会变得糟糕

当您引发事件时,您会将执行延迟到可能不是您自己的代码。例如,如果您正在编写一些库或框架,而这些库或框架将被其他人使用,这一点尤其正确。在事件处理程序中,您完全无法控制发生了什么。事件处理程序可以启动一个全新的线程并等待该线程完成(即,
Join()
),然后返回。如果新线程调用的某个函数与您的
lock
锁定在同一个变量上,那么就对了。僵局


但除此之外,最好的做法是尽量减少在
锁中花费的时间,以减少锁的“瓶颈”方面。如果在
内引发事件,则所有下注都将关闭。

如果在
内调用事件处理程序并引发异常,则锁将被释放。
lock
关键字基本上是
System.Threading.Monitor.Enter/Exit
的简写符号,包装在try-finally块中。也就是说,Exit()方法是从finally块调用的,因此锁会被释放。实际上,这不一定是真的。如果将异常传播给拥有锁yes的调用方,则将释放该异常。如果异常发生在另一个线程上,并使调用方处于死锁状态(即等待返回),则不会发生。@Don Kirkby:完全不同的问题。你提到了“串口”,你是用C语言写的?有什么好的网站、参考资料、书籍等可以看吗?我需要在未来几个月内解决这个问题。任何帮助都将不胜感激。很抱歉劫持了这个线程。@Matt:这并不复杂。只需写一个字符串或字节数组,然后读取DataReceived事件的响应或寄存器。我认为你真的只需要手册:System.IO.Ports.SerialPort类使用起来相当简单-我以前从未见过它,所以我的命令启动并运行得非常快。由于'DiscardNull'属性,我丢失了一些字节,并且花了一段时间才意识到即使有一些数据在等待,'DataReceived'事件也可能不会被调用;调用方无法控制的代码。