C#线程开发:winform简单的C#线程开发实例

来源:互联网 发布:千里眼监控软件客户端 编辑:程序博客网 时间:2024/06/06 08:23

一直想弄清楚线程是如何工作的,一直在找相关的实例都没有找到,没有找到容易看懂的实例。今天终于找到一个,觉得很简单,把它转到这里来分享一下。
要实现的效果是:
点击按纽,窗口上的label上出现1~100数字的变化。

窗口上有两个控键,一个label,一个button。

第一个实例(把窗口上的label上文字改成100):

 

using System;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "100";        }    }}


这个是最简单的实例,应该不难看懂。

第二个实例(点击button,循环显示0动态变化到100数字):

 

using System;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";        }        private void button1_Click(object sender, EventArgs e)        {            for(int i=0;i<101;i++)            {                label1.Text = i.ToString();            }        }    }}


 

运行一下,点击一下button1,没有看到0~100动态变化,就直接到了100了。
原因:因为你的处理器速度太快了,就只能看到最后的结果,那么,怎样才能看到中间过程呢?
我们先用函数的方式来实现上面的功能
写个名为run的函数吧:
private void run(){    for(int i=0;i<101;i++)    {        label1.Text = i.ToString();    }}

 
这样就可以直接调用run函数实现功能了,而不用在事件函数内写代码。(这样做是有好处的)
整个代码如下:
第三个实例(使用函数)

 

using System;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";        }        private void button1_Click(object sender, EventArgs e)        {            run();    //调用run函数        }        private void run()        {            for(int i=0;i<101;i++)            {                label1.Text = i.ToString();            }        }    }}


这里就需要在循环过程中加延时了,假定我们每隔1s的延时,lable1的值增加1。
方法有很多,我们就用一个timer来实现延时。
添加一个timer, 命名为timer1,在timer1的tick事件内添加语句,改变label1的值。(Tick事件是每经过指定时间间隔后被触发)
第四个实例(使用timer),实现每隔1s的延时,lable1的值增加1,以达到动态变化的效果

 

using System;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        int i;          //全局变量i        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";        }        private void button1_Click(object sender, EventArgs e)        {            run();        }        private void run()        {            i = 0;            timer1.Interval = 1000; //设置timer1的间隔时间            timer1.Start(); //启动timer1        }        private void timer1_Tick(object sender, EventArgs e) //timer1的Tick事件        {            i++;            if (i > 100)            {                timer1.Stop();            }            label1.Text = i.ToString();        }    }}


同样的,我们运行一下,看看结果,很好,我们能够看到0~100循环的过程了。

以上是我们平常的做法,让label动态变化的效果,下面我们开始使用线程来实现上面的功能。

感谢wangsdong投稿
由于要使用多线程,我们需要引用System.Threading;所以之后的代码都会在前面加上using System.Threading;
通过下面的语句就定义一个名为thread1的线程
private Thread thread1;
和定义函数极为相似
定义线程之后,就要进行实例化:
thread1 = new Thread(new ThreadStart(run));
这个语句的意思就是实例化thread1并将run函数设定为thread1的入口函数(大概意思就是,让run函数在线程thread1上执行,我是这样理解的)

创建线程就算完成了,那么怎么运行线程呢?
其实和启动timer1是类似的,thread1.Start();就运行了我们创建的线程thread1。

好了,大功告成!哈哈,别着急,既然我们创建了线程,那么在关闭窗口的时候,就要撤消线程。
添加FormClosing事件,在事件内部写如撤消线程的代码:

 

private void Form1_FormClosing(object sender, FormClosingEventArgs e){if (thread1.IsAlive) //判断thread1是否存在,不能撤消一个不存在的线程,否则会引发异常{thread1.Abort(); //撤消thread1}}


这样才算大功告成嘛,整理的代码如下:(在第三个实例的基础上加以改动)
第五个实例

using System;using System.Threading;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        private Thread thread1;        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";        }        private void button1_Click(object sender, EventArgs e)        {            thread1 = new Thread(new ThreadStart(run));            thread1.Start();        }        private void run()        {            for (int i = 0; i < 101; i++)            {                label1.Text = i.ToString();            }        }        private void Form1_FormClosing(object sender, FormClosingEventArgs e)        {            if (thread1.IsAlive)            {                thread1.Abort();            }        }    }}


运行看看,按button1,出错了,怎么回事呢????
哈哈~~看看出错原因,是在run函数内的label1.Text = i.ToString();语句上出的错,没错啊,语法正确啊
哈哈~~我来解释一下,出错的原因是为了保护数据的安全所以不能跨线程调用控件,而label1.Text = i.ToString();句则是在线程thread1上面调用主线程的控件,肯定会出错的!!
怎么办呢?用委托啊(有关委托,请参考其它资料,我就不多说了)
我的理解就是,线程thread1不能调用主线程的lable1,所以,就委托主线程来改变lable1的值。
首先看一个例子:(从例3改写)(并不创建线程,仅有主线程)
创建一个函数,用来设置lable1的值;

private void set_lableText(string s)
{
label1.Text = s;
}
当需要改变lable1的值时,就调用它,并传递要改变的值。

第六个实例:

 

using System;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";        }        private void button1_Click(object sender, EventArgs e)        {            run();    //调用run函数        }        private void run()        {            for(int i=0;i<101;i++)            {                set_lableText( i.ToString() );            }        }        private void set_lableText(string s)        {            label1.Text = s;        }    }}

实现的功能与第三个实例是一样的,只是,增加了一个函数。

到这里,需要了解一下委托这个东西,我们就需要委托主线程调用函数set_lableText(string s);来改变lable1的值。

首先声明一个委托:
delegate void set_Text(string s);

创建一个全局委托变量:(应该是变量吧)
set_Text Set_Text; //请注意大小写,set_Text是委托类型,Set_Text是创建的委托(当然,这里的命名是随意的)

类似于创建线程,需要进行实例化:
Set_Text = new set_Text(set_lableText); //括号内的set_lableText是委托要调用的函数(也就是例6写的set_lableText(string s);函数)

现在,就剩下调用委托了,怎么调用委托呢?很简单。
同过Invoke来调用,语句如下:
label1.Invoke(Set_Text, new object[] { i.ToString() });
//Set_Text是调用的委托,object[]则是我们要传递的参数

整理代码如下:(例7)
第七个实例:使用多线程实现0~100动态变化效果

using System;using System.Threading;using System.Windows.Forms;namespace ThreadTest{    public partial class Form1 : Form    {        private Thread thread1; //定义线程        delegate void set_Text(string s); //定义委托        set_Text Set_Text; //定义委托        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            label1.Text = "0";            Set_Text = new set_Text(set_lableText); //实例化        }        private void button1_Click(object sender, EventArgs e)        {            thread1 = new Thread(new ThreadStart(run));            thread1.Start();        }        private void set_lableText(string s) //主线程调用的函数        {            label1.Text = s;        }        private void run()        {            for (int i = 0; i < 101; i++)            {                label1.Invoke(Set_Text, new object[] { i.ToString() }); //通过调用委托,来改变lable1的值                Thread.Sleep(1000); //线程休眠时间,单位是ms            }        }        private void Form1_FormClosing(object sender, FormClosingEventArgs e)        {            if (thread1.IsAlive) //判断thread1是否存在,不能撤消一个不存在的线程,否则会引发异常            {                thread1.Abort(); //撤消thread1            }        }    }}

这样,一个简单的多线程程序就算完成了。