来,先看代码:
public Launch() { Log4.LogEventHandler += Log4_LogEventHandler; InitializeComponent(); } protected override void OnInitialized(EventArgs e) { Task.Run(() => { Task.Run(() => { Thread.Sleep(1000); Log4.Info("111"); }); Dispatcher.Invoke(() => { Thread.Sleep(2000); Log4.Info("222"); }); }); } void Log4_LogEventHandler(log4net.Core.LoggingEvent arg) { if (arg.Level == Level.Debug) return; Dispatcher.Invoke(() => { _lbLog.Content = arg.RenderedMessage; }); }
如果在WPF主线程使用了这样多线程模型,你的UI线程有可能卡死,当然,这里也与我使用的 Log4 有关。
注意:每次调用 Log4.Info("xxx"), Log4_LogEventHandler 必被调用,它只是 Log 的一个回调函数。
注意,这里就是关键点!!!
OnInitialized 方法里我开启了二级线程,并在一级线程调用了 Dispatcher.Invoke,这个方法是同步的。
1、Thread.Sleep(1000) 是为了模拟先让一级线程调用 Dispatcher.Invoke,但内部 Sleep 了2000,此时二级线程已经开启。
2、Thread.Sleep(2000) 是为了先让让二级线程调用 Log4.Info("111"),然后导致 Log4_LogEventHandler 函数被执行,间接调用了 Dispatcher.Invoke 方法,但此时线程会被 Hold 住(但这里Hold 住的并不是 UI 线程而是二级线程,此时 UI 线程还是正常的),因为一级线程的 Dispatcher.Invoke 方法还没有执行完成,因为在上述步骤时,一级线程抢先占用了 Dispatcher.Invoke。
3、Thread.Sleep(2000) 完成,继续调用 Log4.Info("222"),这里就是问题了,可能是因为 Log4 的机制问题,每次调用 Log 方法必须上次调用完成后,才能继续执行下一个 Log 方法,因为二级线程的 Log4.Info("111") 方法并没有完成,而是被 Hold 了,一级线程又去 Log4.Info("222"),导致线程又被 Hold ,此时被 Hold 的线程是 UI 线程,最后导致 UI 线程卡死。
完美造成了死锁!!!
解决方案:
建议使用 Dispatcher.BeginInvoke 方法,这个方法是异步的,并不会 Holde 线程。但如果你又有同步需求,这时你就需要时刻注意避免形成上诉的线程模型。这个死锁原因花了我大半天的时间才解开,藏的太深了。