.NET Framework 4.8 WinForms两线程死锁原因排查求助
WinForms应用死锁排查问题
环境信息
- 软件/技术栈:基于.NET Framework 4.8的WinForms应用
- 构建配置:Debug Build
- 死锁同步结构:类私有作用域对象实现的Monitor
- 死锁场景:工作线程(Thread #2)与UI线程发生死锁
更新1:清楚这是糟糕的设计,会进行修复,已简化示例以便明确死锁位置。本次发帖核心是排查锁未释放导致死锁的原因。
更新2:经验教训:不要混合同步与异步操作。
问题代码
public class Form { private static SomeSharedObject sharedObj = new SomeSharedObject(); Init() // called once { // UI thread starts initialization // register for notifications (later Thread #2, background worker thread, will callback Device_Attach causing a dead lock between itself and UI thread) sharedObj.Attach += new EventHandler<USBConnectionEventArgs>(Device_Attach); // kick off background worker task bw.RunWorkerAsync(); // UI thread spawns the 2nd thread (Thread #2) that will dead lock with the UI thread // UI thread continues to run ... // UI thread calls a method on a shared static instance (between UI thread and Thread #2) to access some property var devices = sharedObj.ConnectedDevices; // DEAD LOCKS! See pseudo implementation of SomeSharedObject } private void bw_DoWork(object sender, DoWorkEventArgs e) { sharedObj.ReadState(); // Thread #2 reading the state } void Device_Attach(object sender, USBConnectionEventArgs e) { _RefreshHwDeviceList(); // Still Thread #2, but now refreshing the UI } delegate void RefreshHwDeviceListCallBack(); private void _RefreshHwDeviceList() { // Thread #2 enters here to update the UI // Since we're here on background worker thread and need to update UI, switch to UI thread. // NOTE: Bam, we're in a dead lock (at line HwDeviceList.Invoke())! UI thread is still blocked in SomeSharedObject.ConnectedDevices (on the _accessLock), but // that doesn't make sense since Thread #2 released the _accessLock before calling Attach callback (why is this lock still held by Thread #2) // How do I know? // When I break in VS and open threads watch window, VS clearly says that the main UI thread is blocked on Thread #2 at the location // of the _accessLock in SomeSharedObject.ConnectedDevices property. if (HwDeviceList.InvokeRequired) { var callback = new RefreshHwDeviceListCallBack(_RefreshHwDeviceList); HwDeviceList.Invoke(callback, new object[] { }); } else { // update UI on UI thread } } } public class SomeSharedObject { private readonly object _accessLock = new object(); public event EventHandler<USBConnectionEventArgs> Attach; public IEnumerable<IDevice> ConnectedDevices { get { lock (_accessLock) // NOTE: This line is where the UI thread will wait indefinitely wait for Thread #2, but Thread #2 should of released this lock (see ReadState())! { return _deviceList .Where(x => x.IsConnected || ((x as IUSBDevice)?.IsAttached ?? false)) .ToArray(); } } } public void ReadState() { var attachingDevice = e.Device; lock (_accessLock) // Thread #2 grabs the lock first, so UI thread is blocked, okay that's fine as long as at the end of its scope the lock is released { // read some state } //NOTE: release the lock here before raising the event below so we don't DEAD lock the UI thread (but the lock is not released, why????) // Thread #2 now calls back to the Form's handler if (Attach != null) { Attach(this, e); } } }
内容的提问来源于stack exchange,提问作者ActiveX
相关产品推荐
相关产品推荐

