我在2个线程之间有一个死锁,我真的不知道为什么会出现死锁

问题描述 投票:0回答:1

全部,

软件/技术:.net Framework 4.8 上的 Winforms 应用程序

构建配置:调试构建

发生死锁时使用的同步构造:使用类的私有范围对象进行监视

死锁:工作线程(线程 #2)与 UI 线程发生死锁

请参阅下面的死锁代码,请阅读解释流程、死锁发生位置以及为什么我很困惑为什么会发生的注释。

更新:我知道这是一个非常糟糕的设计,我会修复它,并且我过度简化了示例,以使其更清楚发生死锁的位置。这篇文章的目的是弄清楚为什么锁没有释放导致死锁。

更新 2:故事的寓意:不要将同步与异步混合在一起。安息吧

代码:

public class Form
{
    private static SomeSharedObject sharedObj = new SomeSharedObject();
    
    Init() // callled 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 refereshing 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 indefinetly 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);
        }
    }
}
c# multithreading winforms deadlock .net-4.8
1个回答
0
投票

感谢大家的回复,答案在评论里。 太长了;当将同步与异步混合时,您不能仅从执行线程的角度来看待问题,您必须考虑整个异步基础设施(例如同步上下文及其机制)。 基本上,避免将异步代码与同步代码混合在一起。

© www.soinside.com 2019 - 2024. All rights reserved.