我经常听说在调用事件监听器之前解锁所有锁是个好主意,以避免死锁。但是,由于lock {}
块可以由C#中的同一个线程重入,可以从锁定的块调用事件,还是需要复制相关的状态数据并调用锁块外的事件?
如果没有,请举例说明从lock {}
块内调用事件会有什么问题。
由于
答案 0 :(得分:9)
我不记得曾经需要在lock
声明中提出一个事件。但不难想象事情会发生严重变化。
当您举起活动时,您会将执行推迟到可能不是您自己的代码。如果您正在编写某些库或框架(例如,将由其他人使用),则尤其如此。在事件处理程序内部,您完全无法控制发生的情况。事件处理程序可以启动一个全新的线程并在返回之前等待该线程完成(即Join()
)。如果该新线程调用某个函数,该函数锁定在与lock
,bingo相同的变量上。死锁。
但除此之外,最佳做法是尽量减少在lock
内花费的时间,以减少锁定的“阻塞点”方面。如果您在lock
内举起一个活动,则所有投注均已关闭。
答案 1 :(得分:4)
问题不在于事件处理程序可能会尝试调用您已经拥有的锁,问题是事件处理程序可能尝试获取一些其他锁(可能阻塞并设置死锁),或者事件处理程序可能会像数据库查询一样启动一些长时间运行的任务(让你的锁无法访问其他线程,直到完成)。一般规则是你应该尽可能短的时间锁定。
将线程与您无法控制的事件处理程序混合肯定会让您感到麻烦。我目前遇到了一些麻烦,因为我从串口的接收线程中提出了一个事件。某些事件处理程序代码决定阻塞并等待,直到从串行端口收到另一条消息。这将是一个漫长的等待,因为你只是阻止了一个只接收线程!我甚至不能生气,因为我写了两段代码(相隔一年,所以我有时间忘记细节)。
答案 2 :(得分:1)
是的,从持有的锁中调用事件不会被认为是好的设计。主要是因为锁定部分有一个转换,如果出现错误或出现问题,锁定不一定会被释放。
一般情况下,您希望最大程度地缩短锁定时间以防止锁定时间过长(导致延迟),并且还可以减少锁定时可能出现代码失败的可能性。