WinForm“假死”问题汇总

一、一处消息死锁分析

最近维护一个工控机上运行的winform程序,我的前任在一个弹出窗口(窗口B)里面调用了ShowDialog方法弹出对话框(窗口C),导致了一个问题是有时关闭窗口C时程序假死(无规律),最后用windbg远程调试找到了问题。

解决方法如下:用一个委托来执行ShowDialog。

public delegate DialogResult DelegateShowMessageForm(string msg);

参考资料:一处消息死锁分析

 

最近做winform程序时,在主窗口用线程加了个刷新电量的线程(用于实现充电状态的效果),后面导致其他窗口关闭时假死。

用DebugView抓取Debug信息后发现,该窗口的From_Closing事件和Close方法都执行完了,但窗口未关闭。最后将刷新电量的线程取消,改用下文方法,贴上部分代码。

        int i = 0; //用于充电时刷新电池图片
        private void ChangeBatteryPic(IDModulePower power)
        {


            if (!currIDModulePower.Equals(power))
            {
                int picNum = power.QuantityOfBattery;
                switch (power.PowerStatus)
                {
                    case IDModulePowerStatus.ExternalPower:
                        picNum = 7;
                        RefreshBatteryPic(picNum);
                        break;
                    case IDModulePowerStatus.BatteryPower:
                        RefreshBatteryPic(picNum);
                        break;
                    case IDModulePowerStatus.OnCharging:
                        {
                            if (i < 6)
                            {
                                i++;
                            }
                            else
                            {
                                i = 0;
                            }
                            RefreshBatteryPic(i);
                        }
                        break;
                    case IDModulePowerStatus.ChargeException:
                        picNum = 6;
                        RefreshBatteryPic(picNum);
                        break;
                    default:
                        break;
                }

                currIDModulePower = power;
            }

        }

        public delegate void RefreshControl(int i);

        private void RefreshBatteryPic(int picNum)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new RefreshControl(RefreshBatteryPic), picNum);
            }
            else
            {
                this.pbBattery.BackgroundImage = VALWELL.SSLC.Resource.Resources.CurrBatteryStatus(picNum);
                this.pbBattery.BackgroundImageLayout = ImageLayout.Center;
                Application.DoEvents();
            }
        }

Control的Invoke和BeginInvoke

对于这两个方法,首先我们要有以下的认识:
    1. Control.Invoke,Control.BeginInvoke和delegate.Invoke,delegate.BeginInvoke是不同的。
    2. Control.Invoke中的委托方法,执行在主线程,也就是我们的UI线程。而Control.BeginInvoke从命名上来看虽然具有异步调用的特征(Begin),但也仍然执行在UI线程。
    3. 如果在UI线程中直接调用Invoke和BeginInvoke,数据量偏大时,依然会造成UI的假死。
  有很多开发者在初次接触这两个函数时,很容易就将它们同异步联系起来、有些人会认为他们是独立于UI线程之外的工作线程,实际上,他们都被这两个函数的命名所蒙蔽了。如果以传统调用异步的方式,直接调用Control.BeginInvoke,与同步函数的执行无异,UI线程还是会处理所有辛苦的操作,造成我们的应用程序阻塞。
  Control.Invoke的调用模型很明确:在UI线程中以代码顺序同步执行,因此,抛开工作线程调用UI元素的干扰,我们可以将Control.Invoke视为同步,本文不做过多介绍。
  很多开发者在接触异步后,再来处理窗体假死的问题,很容易想当然的将Control.BeginInvoke视为WinForm封装的异步。

 

posted @ 2016-01-22 12:36  十二楼C  阅读(1216)  评论(0编辑  收藏  举报