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

一直想弄清楚线程是如何工作的,一直在找相关的实例都没有找到,没有找到容易看懂的实例。今天终于找到一个,觉得很简单,把它转到这里来分享一下。
要实现的效果是:
点击按纽,窗口上的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
            }
        }
    }
}

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

你可能感兴趣的:(C#)