最近在一个项目中,由于调用的方法需要等待较长时间才有返回值,这就造成界面假死的现象,在用户体验方面
很不友好。为此我新增了一个UI作为等待界面,在调用该方法时在新线程里打开这个UI,显示等待界面,方法执行完关闭该UI。原理很简单,实现的时候却遇到不少问题。
我写了一个简单的测试程序,主界面为Form1,等待界面为Wait,使用timer1定时模拟等待时间。
测试一:新建线程为th_showwait,代码如下:
Thread th_showwait;
///<summary>
///使用控件模拟调用方法
///</summary>
private void button1_Click(object sender, EventArgs e)
{
ThreadStart();
}
/// <summary>
/// 啟動等待界面線程,模拟2s后线程关闭
/// </summary>
public void ThreadStart()
{
try
{
th_showwait = new Thread(ShowForm);
th_showwait.IsBackground = true;
th_showwait.Start();
Thread.Sleep(2000);
ThreadAbort();
}
catch (Exception ex)
{
}
}
/// <summary>
/// 打開等待界面
/// </summary>
private void ShowForm()
{
try
{
wait = new Wait();
wait.ShowDialog();
}
catch (Exception ex)
{
}
}
结果如下:
开始运行没什么问题,线程和界面能正常显示,但是运行一段时间后,出现了“无法启动正在终止的线程”,意味着线程启动失败了,究其原因还是线程终止的机制,About()是非正常结束,即使使用Join()也无济于事,甚至让界面假死,界面显示不了等待图片,客户简直不能忍受。。。
测试二:没方法,线程频繁启停的方法既不靠谱又消耗系统资源,我只能尝试不用线程启停的方式打开等待界面,这时想到了用全局变量去做,这里有点要注意,开启不能用ShowDialog(),要用Show(),线程运行到ShowDialog()会停止往下执行,直到界面关闭,界面关闭不能用Close(),因为Close()执行了close和dispose方法,这就导致界面被释放,下次打开会出现失败,报错不能访问已经释放的资源,这里使用Hide()隐藏。调试好的代码如下
Wait wait = new Wait();
private int fun_code;
Thread th_funcode;
///
///使用定时器模拟关闭信号
///
private void button1_Click(object sender, EventArgs e)
{
fun_code = 1;
timer1.Interval = 2000;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
fun_code = -1;
timer1.Stop();
}
private void Form1_Load(object sender, EventArgs e)
{
th_funcode = new Thread(ShowAndClose);
th_funcode.IsBackground = true;
th_funcode.Start();
}
private void ShowAndClose()
{
while (true)
{
try
{
this.Invoke((MethodInvoker)delegate
{
//wait2 = new Wait();
if (fun_code==1)
{
fun_code = 0;
wait2.Show(this);
}
if (fun_code==-1)
{
wait2.Hide();
fun_code = 0;
}
});
}
catch (Exception ex)
{
//throw;
}
}
}
自此,程序完美运行。