当我们的程序达到一定的复杂度之后,有时需要一个后台进程来处理一些数据,而窗体界面显示数据的变化,而又不能进入假死状态,或是某个进程比较耗时,需要等待,比如网络传输数据,此时,使用线程处理是必不可少的一种方法。对于初学者来说,如何让后台线程与界面配合使用,是不太容易理解的,如果你能熟练使用这种方法,将会带你迈入程序开发的一个新的阶段,基于此可以解决很多类似问题。
为此,作为初学者的我,经过摸索,把所得的一段代码或者说是一种方法分享出来,希望能给大家一些启发,并希望能相互学习,有什么问题,希望得到大家的指正。当然,可能还有更优雅更完美的方法,有待进一步摸索。
示例假定在一个窗体上,有3个textbox,1个button,当按下button时,依次向textbox填入一些随机值。
对于这类问题,我们首先比较清楚的,简单来说会有两个函数:一个是后台数据处理函数,一个是界面更新的函数,这两个函数直接关联,则会出现假死,如果把第一个函数放置在线程中,直接调用第二个函数,则界面是不允许更新的,此时,必须使用代理,或者说是一种间接的方式。
可能我的表述不十分准确,具体请参考代码:(代码中的某些文字的表述,名词可能不太准确,但可能这样去理解。)
public partial class Form1 : Form
{
Random rd = new Random();
string sa, sb, sc; //定义全局数据,这些数据由后台线程和前端界面共享,免去参数传递的麻烦,数据一般由后台线程更新,前端界面读取显示
Thread thread; //定义一个线程
public delegate void UpdateUI(); //定义一个代理结构
public UpdateUI updateui; //定义一个代理变量
public Form1()
{
InitializeComponent();
updateui = new UpdateUI(viewtext); //实例化代理变量,让它与界面更新的函数相关联
}
private void button1_Click(object sender, EventArgs e)
{
if (thread == null || thread.ThreadState == ThreadState.Stopped)
{
thread = new Thread(process); //实例化线程,让它与后台数据更新的函数相关联
thread.IsBackground = true; //将线程设置为后台线程,让它能在主程序关闭时,它能自动关闭
thread.Start();
}
}
private void process() //后台处理函数
{
int i, n;
for (i = 0; i < 100; i++)
{
n = rd.Next(20);
switch (i % 3)
{
case 0:
sa += " " + n.ToString();
break;
case 1:
sb += " " + n.ToString();
break;
case 2:
sc += " " + n.ToString();
break;
}
this.Invoke(updateui); //当数据发生了变化,交由代理 处理界面的更新
Thread.Sleep(100);
}
}
private void viewtext() //实际的界面更新函数
{
textBox1.Text = sa;
textBox2.Text = sb;
textBox3.Text = sc;
}
}