为什么要用多线程
- 让计算机"同时"做多件事情,节约时间。
- 多线程可以让一个程序“同时”处理多个事情。
- 后台运行程序,提高程序的运行效率,也不会使主界面出现无响应的情况。
- 获得当前线程和当前进程
如何实现多线程?
- 编写产生线程所要执行的方法
- 引用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:获得当前的线程引用