zoukankan      html  css  js  c++  java
  • 多线程基础

    序:

            我是自学的C#,在看到多线程一章时,郁闷了,搞不懂,很难理解吧...自认为有软件天赋,却没看懂...唉...自信心被打击了,到网上查了很多的资料,包括MSDN

    上也查过资料,可惜还是没搞懂多线程...

            于是,硬着头皮一遍一遍的看多线程那一章,终于在看完第31遍,我写出了第一个多线程程序,还算小有所获,鉴于对网上很多资料没办法理解(因为,很多资料一来就大篇大篇

    的代码,让人头晕,我是这么觉得)所以,我自己就写了这篇心得体会,希望能给大家带来些许帮助.

    匆忙之中,错误难免,欢迎指正,共同进步.

    正文:

            首先我要提一点,关于线程的基础知识,一个程序,即一个进程,可以有很多个线程,当然,至少要有一个线程,即主线程.相信大家都知道多线程的好处吧,举个书上的例子

    吧,Windows在复制文件的时候,有个动画,是在复制文件过程中进行的,也就是一边复制文件,一边播放动画,这个就是很简单的多线程,如果没有动画,复制一个大文件的时候,我们知

    道计算机是死机了,还是仍然在复制呢???多线程就很好的解决了这个问题.懂了吧,恩,很好!!那么,我们就开始吧!

            首先,我们写个简单的单线程程序,也就是只有程序自己创建的那个主线程,没有使用多线程.

    创建一个新工程,向窗口添加一个label命名为label1;我们要让程序运行时label1就显示一个数字,假设为100;通常我们会直接在窗口加载事件中写label1.Text = "100";这样,运行

    ,label1果然显示了100;

    代码如下:(例1)

    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,命名为button1,我们要在按下button1后,将lable1的text从0显示到100,
    那么,我们需要添加button1的Click事件,在click事件内写入循环显示0到100.

    代码如下:(例2)

    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,结果是我们一下就看到了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 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事件是每经过指定时间间隔后被触发)

    代码如下:(例4)

    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循环的过程了。

    下面我们就要进入多线程了,不知道各位将上面的内容看懂了没有?

    开始进入多线程之前我还是先简单的说说定义线程吧。(与多线程有关的其它内容我就不说了吧,那个太多太多了)
    由于要使用多线程,我们需要引用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
        }
    }

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

    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的值时,就调用它,并传递要改变的值。

    整理代码如下:(例6)

    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;
            }
        }
    }

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

    
    

    现在再来看看委托,我们就需要委托主线程调用函数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)

    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
                }
            }
        }
    }

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

    结语:
        希望本文能给那些徘徊在多线程门口的朋友带来些许帮助,也希望大家能多多分享自己的心得体会。

     
  • 相关阅读:
    redis发布订阅
    redis学习笔记(面试题)
    redis安全 (error) NOAUTH Authentication required
    HDU3001 Travelling —— 状压DP(三进制)
    POJ3616 Milking Time —— DP
    POJ3186 Treats for the Cows —— DP
    HDU1074 Doing Homework —— 状压DP
    POJ1661 Help Jimmy —— DP
    HDU1260 Tickets —— DP
    HDU1176 免费馅饼 —— DP
  • 原文地址:https://www.cnblogs.com/cnsend/p/12347535.html
Copyright © 2011-2022 走看看