2006年06月29日

这两天写了一个小程序,试用一下BackgroundWorker这个控件,发现一些文档之外的东西。
程序好象MSDN中的示例一样,有两个按钮,一个进度条。一个按钮是Start,一个按钮是Cancel,分别调用BackgroundWorker的RunWorkerAsync和CancelAsync。
BackgroundWorker几个事件都有,代码如下:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            int i = 0;
            while (i < 1000000)
            {
                if (this.backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                };
                i++;

                if (i % 1000 == 0)
                {
                    //System.Threading.Thread.Sleep(0);

                    backgroundWorker1.ReportProgress(i);
                };

            };
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
                this.progressBar1.Value = e.ProgressPercentage;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
          
            if(e.Cancelled)
                MessageBox.Show("Canceled");
            else
                if(e.Error!=null)
                    MessageBox.Show("Error");
                else
                    MessageBox.Show("Finished");
        }

文档中说即使调用了CancelAsync,可能是在DoWork事件最后一次检测CancellationPending之后,这样DoWork仍将执行完毕。在执行上面的程序时,点击Cancel按钮很难使进度停下来,在第一次Start后停的概率大一些,在几次Start后,Cancel按钮基本不响应,不管怎么点,DoWork将是执行完毕,显示“Finished”对话框。
开始我想,是不是DoWork早就执行结束了,而ReportProgress是按消息方式执行了,所以虽然点击了Cancel按钮,实际最后一次CancellationPending检测已经过去,结果就是看到进度条不停地走到头,再显示“Finished”。
但是这个解释不完美,程序响应也不完美,大多数时候,在点击了Start之后,Cancel按钮就不响应了,这没有达到使用BackgroundWorker控件的目的,使用这个控件本来就是为了生成更好用户响应的程序。
于是我在DoWork事件中加入了注释的一行,对Sleep的调用。这时不管进度条走到哪儿,都可以点击Cancel按钮,而且Cancel按钮也始终有用户响应指示(当鼠标移动到它上面时),点击之后,DoWork事件被中止,程序结果提示“Cancel”对话框。
Thread.Sleep(0)起的作用是主动把时间片交给其它线程,在没有它时,也许操作系统觉得执行DoWork的线程更重要,所以会分较长的时间片给它,而用户界面线程的时间片被ProgressChanged事件占用完了,从而导致用户界面失去响应。
为进一步证实以上猜测,我将DoWork事件中的ReportProgress和Sleep同时注释掉,这时虽然看不到进度变化,但Cancel按钮有响应,而且可以将DoWork事件中止。
更准确的解释需要对Windows线程调度细节更进一步的了解。
另外还有一个需要注意的地方是,调用CancelAsync之后,在DoWork事件中可以进行CancellationPending的检测,但当运行到RunWorkerCompleted事件时,CancellationPending状态已经改变了。所以必须要在DoWork中对DoWorkEventArgs 的Cancelled进行设置,以便在RunWorkerCompleted中进行检测,判断是执行完毕还是中途取消了。

2006年06月06日

豆豆是我的宝贝女儿,今年两岁,长得很是小巧,但颇有些神奇的地方,今天说的就是其中之一。


在我的家乡,鱼刺被称为“鱼卡”,简称“卡”。豆豆上次从老家回来,一岁半,不知道从什么时候开始,在喂她吃鱼时,她都会看着你说:“卡,有卡,卡不能吃”,那个“吃”字还拐着弯,奶声奶气,但又说得十分认真,听着非常好笑。每夹一次,她都会说一遍提醒你,生怕你不知道,而且声音不时还会突然提高,唯恐你听麻木了。


有几次,我真的不小心把卡混在鱼肉里喂给她,都被豆豆准确地发现,嘴里念着“卡,有卡”,然后把鱼肉和卡吐了出来。一边为自己的大意冒冷汗的同时,一边感叹着豆豆的神奇。


豆豆从生下来就“谨小慎微”,对自己特别小心。一次洗澡时,我一大意没扶住她,结果她一滑,差点淹到澡盆里。从此,豆豆每次洗澡,刚一下水,都是两手紧握盆边,以防不测。


一次给豆豆冲奶,温度有点高,把她烫了一下,从此豆豆在喝奶时,都先把奶嘴挡在外面,感觉一下温度,再慢慢把奶嘴含入嘴中。


不知道从什么时候开始,因为什么事情,豆豆每次在开始吃一样东西时,都是先试着尝一下,味道好才会吃,不会听大人的花言巧语。


也许正是我们这种大意的父母,才会培养出这种细心的儿女。