开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 4239|回复: 12
收起左侧

[源码分享] (转)C# 多线程基础,仅以此心得献给那些渴望学习多线程的朋友

[复制链接]
发表于 2011-1-11 14:30:58 | 显示全部楼层 |阅读模式   江西省南昌市
我是自学的C#,在看到多线程一章时,郁闷了,搞不懂,很难理解吧...自认为有软件天赋,却没看懂...唉...自信心被打击了,到网上查了很多的资料,包括MSDN
    上也查过资料,可惜还是没搞懂多线程...
    于是,硬着头皮一遍一遍的看多线程那一章,终于在看完第31遍,我写出了第一个多线程程序,还算小有所获,鉴于对网上很多资料没办法理解(因为,很多资料一来就大篇大篇
    的代码,让人头晕,我是这么觉得)所以,我自己就写了这篇心得体会,希望能给大家带来些许帮助.
    匆忙之中,错误难免,欢迎指正,共同进步.
    正文:
    首先我要提一点,关于线程的基础知识,一个程序,即一个进程,可以有很多个线程,当然,至少要有一个线程,即主线程.相信大家都知道多线程的好处吧,举个书上的例子
    吧,Windows在复制文件的时候,有个动画,是在复制文件过程中进行的,也就是一边复制文件,一边播放动画,这个就是很简单的多线程,如果没有动画,复制一个大文件的时候,我们知
    道计算机是死机了,还是仍然在复制呢???多线程就很好的解决了这个问题.懂了吧,恩,很好!!那么,我们就开始吧!
    首先,我们写个简单的单线程程序,也就是只有程序自己创建的那个主线程,没有使用多线程.
    创建一个新工程,向窗口添加一个label命名为label1;我们要让程序运行时label1就显示一个数字,假设为100;通常我们会直接在窗口加载事件中写label1.Text = "100";这样,运行
    ,label1果然显示了100;
    代码如下:(例1)
    using System;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : Form
    {
    public Form1()
    {
    InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
    label1.Text = "100";
    }
    }
    }
    很简单吧,看懂了吗??
    什么,没有,啊~~~神啊~~~救救我吧,那请你在翻书,把最最最最最基础的书翻出来看看里面的最最最最最简单例子(以后不要说我认识你)
    好了,看懂的朋友继续往下看:
    我们现在要将程序稍稍改动一下,添加一个Button,命名为button1,我们要在按下button1后,将lable1的text从0显示到100,
    那么,我们需要添加button1的Click事件,在click事件内写入循环显示0到100.
    代码如下:(例2)
    using System;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : 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,结果是我们一下就看到了100,并没有看到0~100的过程,为什么呢?
    呵呵,因为你的处理器速度太快了,就只能看到最后的结果,那么,怎样才能看到中间过程呢?(等一下再讲)
    我们先用函数的方式来实现上面的功能
    写个名为run的函数吧:
    private void run()
    {
    for(int i=0;i<101;i++)
    {
    label1.Text = i.ToString();
    }
    }
    这样就可以直接调用run函数实现功能了,而不用在事件函数内写代码。(这样做是有好处的)
    整个代码如下:(例3)
    using System;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : 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事件是每经过指定时间间隔后被触发)
    代码如下:(例4)
    using System;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : 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循环的过程了。
    下面我们就要进入多线程了,不知道各位将上面的内容看懂了没有?
    开始进入多线程之前我还是先简单的说说定义线程吧。(与多线程有关的其它内容我就不说了吧,那个太多太多了)
    由于要使用多线程,我们需要引用System.Threading;所以之后的代码都会在前面加上using System.Threading;
    怎么定义线程呢?通过下面的语句就定义一个名为thread1的线程
    privateThread thread1;
    和定义函数极为相似
    定义线程之后,就要进行实例化:
    thread1 = newThread(newThreadStart(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
    }
    }
    这样才算大功告成嘛,整理的代码如下:(例5)(在例3的基础上加以改动)
    using System;
    using System.Threading;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : 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 = newThread(newThreadStart(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的值时,就调用它,并传递要改变的值。
    整理代码如下:(例6)
    using System;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : 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;
    }
    }
    }
    实现的功能与例3是一样的,只是,增加了一个函数。
    现在再来看看委托,我们就需要委托主线程调用函数set_lableText(string s);来改变lable1的值。
    首先声明一个委托:
    delegate void set_Text(string s);
    创建一个全局委托变量:(应该是变量吧)
    set_Text Set_Text; //请注意大小写,set_Text是委托类型,Set_Text是创建的委托(当然,这里的命名是随意的)
    类似于创建线程,需要进行实例化:
    Set_Text = newset_Text(set_lableText); //括号内的set_lableText是委托要调用的函数(也就是例6写的set_lableText(string s);函数)
    现在,就剩下调用委托了,怎么调用委托呢?很简单。
    同过Invoke来调用,语句如下:
    label1.Invoke(Set_Text, new object[] { i.ToString() });
    //Set_Text是调用的委托,object[]则是我们要传递的参数
    整理代码如下:(例7)
    using System;
    using System.Threading;
    using System.Windows.Forms;
    namespace ThreadTest
    {
    public partial classForm1 : Form
    {
    privateThread thread1; //定义线程
    delegatevoidset_Text(string s); //定义委托
    set_Text Set_Text; //定义委托
    public Form1()
    {
    InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
    label1.Text = "0";
    Set_Text = newset_Text(set_lableText); //实例化
    }
    private void button1_Click(object sender, EventArgs e)
    {
    thread1 = newThread(newThreadStart(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
    }
    }
    }
    }
    这样,一个简单的多线程程序就算完成了。
    结语:
    希望本文能给那些徘徊在多线程门口的朋友带来些许帮助,也希望大家能多多分享自己的心得体会。
结帖率:44% (4/9)
发表于 2011-1-12 15:30:47 | 显示全部楼层   江苏省扬州市
{:3_234:}是你原创的啊???  字真多啊
回复 支持 反对

使用道具 举报

结帖率:100% (1/1)
发表于 2011-1-29 17:41:04 | 显示全部楼层   甘肃省白银市
{:3_242:}{:3_242:}
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)
发表于 2011-2-27 11:46:31 | 显示全部楼层   重庆市重庆市
学习一下。。
回复 支持 反对

使用道具 举报

结帖率:50% (5/10)
发表于 2011-3-10 15:44:00 | 显示全部楼层   湖南省长沙市
不                错             哦          看看
回复 支持 反对

使用道具 举报

结帖率:50% (5/10)
发表于 2011-3-10 15:58:08 | 显示全部楼层   湖南省长沙市
不                错             哦          看看
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)
发表于 2011-3-26 01:39:08 | 显示全部楼层   贵州省遵义市
支持,努力
回复 支持 反对

使用道具 举报

结帖率:86% (6/7)
发表于 2013-7-8 20:59:34 | 显示全部楼层   四川省成都市
虽然废话很多。但是还是学到了一些东西,多谢
回复 支持 反对

使用道具 举报

发表于 2013-12-31 13:32:07 | 显示全部楼层   上海市上海市
新手看懂啊,在哪不懂?
回复 支持 反对

使用道具 举报

发表于 2013-12-31 13:33:54 | 显示全部楼层   上海市上海市
注意没写引用system.threading
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表