39.多线程

时间:2020-05-28
本文章向大家介绍39.多线程,主要包括39.多线程使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

为什么要用多线程

  • 让计算机"同时"做多件事情,节约时间。
  • 多线程可以让一个程序“同时”处理多个事情。
  • 后台运行程序,提高程序的运行效率,也不会使主界面出现无响应的情况。
  • 获得当前线程和当前进程

如何实现多线程?

  • 编写产生线程所要执行的方法
  • 引用System.Threading命名空间
  • 实例化Thread类,并传入一个指向线程所要运行方法的委托。(这时候这个线程已经产生,但是还没有运行)
  • 调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定。

前台线程和后台线程

前台线程

只有所有的前台线程都关闭才能完成程序关闭。主线程也是前台线程

后台线程

只要所有的前台线程结束,后台线程自动结束。

实现

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp8 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            //创建一个线程去执行这个方法,默认情况下新创建的线程都是前台线程
            Thread th = new Thread(Test);
            //标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程,
            //由cpu决定
            //将线程设置为后台线程
            th.IsBackground = true;
            th.Start();
        }

        private void Test() {
            for (int i = 0; i < 500000; i++) {
                Console.WriteLine(i);
            }
        }


    }
}

当创建的线程为前台线程时,会出现如下图所示现象,当关闭窗体时,创建的那个线程依旧在执行。而当将此线程设为后台线程后,就不会出现这种现象了。

在.Net下,是不允许跨线程的访问。

例如:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp8 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            //创建一个线程去执行这个方法,默认情况下新创建的线程都是前台线程
            Thread th = new Thread(Test);
            //标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程,
            //由cpu决定
            //将线程设置为后台线程
            //th.IsBackground = true;
            th.Start();
        }

        private void Test() {
            for (int i = 0; i < 10000; i++) {
                //Console.WriteLine(i);
                textBox1.Text = i.ToString();
            }
        }


    }
}

运行:

textbox1控件是由主线程创建的,它所在的Test()方法被新线程执行了,当创建的新线程想要访问另一个线程(主线程)中的资源时,应用程序并不允许这样做。在.Net下,是不允许跨线程的访问。

可以这样进行修改

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp8 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            //创建一个线程去执行这个方法,默认情况下新创建的线程都是前台线程
            Thread th = new Thread(Test);
            //标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程,
            //由cpu决定
            //将线程设置为后台线程
            //th.IsBackground = true;
            th.Start();
        }

        private void Test() {
            for (int i = 0; i < 10000; i++) {
                //Console.WriteLine(i);
                textBox1.Text = i.ToString();
            }
        }

        private void Form1_Load(object sender, EventArgs e) {
            //取消跨线程的访问的限制
            //Control是winform中控件的基类
            //CheckForIllegalCrossThreadCalls:指示是否捕获对错误线程的调用
            Control.CheckForIllegalCrossThreadCalls = false;
        }
    }
}

但是当关闭程序时,有时也会出现问题如下图。

出现这种问题的原因是:虽然关闭了程序,但由于种种原因,新线程并没有马上关闭,也就意味着新线程还会访问textbox1,但是主线程一关,资源将被释放掉,textbox1也就不存在了,新线程就访问不到textbox1了。
所以可以在关闭程序时判断下这个新线程是否为null,如果这个新线程th为null,那么就是主线程结束了,这个新线程也结束了。如果不为null,那么意味着主线程虽然关了,但由于某些原因,新线程并没有马上关闭,这时我们可以手动的进行关闭。

解决:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp8 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }
        Thread th;
        private void button1_Click(object sender, EventArgs e) {
            //创建一个线程去执行这个方法,默认情况下新创建的线程都是前台线程
             th = new Thread(Test);
            //标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程,
            //由cpu决定
            //将线程设置为后台线程
            //th.IsBackground = true;
            th.Start();
        }

        private void Test() {
            for (int i = 0; i < 10000; i++) {
                //Console.WriteLine(i);
                textBox1.Text = i.ToString();
            }
        }

        private void Form1_Load(object sender, EventArgs e) {
            //取消跨线程的访问
            //Control是winform中控件的基类
            //CheckForIllegalCrossThreadCalls:指示是否捕获对错误线程的调用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
            //当你点击关闭窗体的时候,判断新线程是否为null
            if (th != null) {
                //结束这个线程,线程被Abort后就不能再重新Start了
                th.Abort();
            }
        }
    }
}

注意 如果线程执行的方法需要参数,那么要求这个参数必须是object类型.

 private void button1_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(Test);
            th.IsBackground = true;
            th.Start("123");
            //Test();
        }


private void Test(object s){
           string ss = (string)s;
           for (int i = 0; i < 10000; i++){
                Console.WriteLine(i);
           }
}

方法

Start():启动线程(告诉CPU 我可以被执行了,具体什么时候执行,由CPU决定)
Abort():终止线程 终止完成之后不能再Start()
Thread.Sleep() 静态方法,可以使当前线程停止一段时间运行
Thread.CurrentThread:获得当前的线程引用

原文地址:https://www.cnblogs.com/lz32158/p/12977574.html