后台线程更新界面的巧妙方法

2012-04-16 11:09

 

在单机版程序的设计中,对于需要较长时间运行的操作,一般都通过后台线程来完成。如果直接用 UI 线程(在 click 事件中) 运行,则 UI 界面长时间得不到机会重新绘制,会造成程序假死的现象(俗称“翻白眼”)。

 

后台线程更新界面有一些注意事项:

1. 后台线程一般不能直接操作界面控件,需要调用 invoke 之类的函数;

2. 后台线程更新界面的频次不能太慢,太慢则也容易让用户觉得程序“死掉了”;

3. 后台线程更新界面不能太快,一来界面更新太快人眼看不清,容易让人觉得程序好像失控了,在胡乱显示一些乱码;二来,界面更新太快,也会影响整个操作的完成速度,更新界面也是需要 CPU 的。我们知道,电影每秒是 24帧,也就是说,每秒更新画面 24 次,是可以让人觉得很流畅的,每秒更新超过 24 次是不必要的。

 

如果只有问题1 ,则还比较好处理。网上很多讲述 invoke 之类的函数。虽然麻烦,也还算在可控范围。

对于问题2/3, 则不容易处理。比如,我的程序是批量复制文件,在我的开发计算机上经过测试,每复制 10 个文件,更新一下界面,看起来比较好。程序就这么写了。

交付给用户之后,如果用户的计算机比我的计算机快了很多,或者慢了很多,则运行界面效果还是不理想:不是更新太快、就是更新太慢。

 

正确的解决办法是:

更新用户界面,采用定时器 timer ,取名 UpdateUiTimer。后台线程运行过程中,把运行状态(百分比、状态提示详细字符串、主要步骤字符串)放入全局变量中,UpdateUiTimer 来读取全局变量并显示。定时器运行间隔,可以设置成每秒 2 –5 次(我的经验值)。invoke 之类的函数就不要调用了。这样可以解决问题。

全局变量类设计,可以为这样的形式(C#代码):


public class GlobalVars
{

    public static int RunningPercent;

    public static string RunningMainStepStatus;

    public static string RunningDetailStepStatus;

}
		

 

这里有几个注意事项:

a. 多线程对同一个变量的读写,如果不加特别控制,是有一点缓存、延迟的。也就是说,一个线程对变量的更改,并不一定会被另一个线程读到。举例来说,工作线程先更新完成进度为 5%, 然后更新为 10%,这时候 timer 去读取进度,有可能读到 5%,下次才能读到 10%。不过这点对我们的显示程序逻辑,不构成多大影响。

b. 后台线程运行的时候,最好把界面 disable,不然用户可能点击两次按钮,或者在后台在运行的时候,做别的事情,有可能会互相影响。

 

 

欢迎转载,转载请注明出处: https://www.zheguisoft.com/staff_blogs/jacklondon_chen/2012, 及 https://www.cnblogs.com/jacklondon/archive/2012/04/16/2451403.html