紧接着进程篇章博客,现在写下一篇线程的博客,不过是简单介绍噢,太深奥的我也不懂啦!!!
什么是线程?
按我个人理解,线程他就像马路,一个车道只能开一辆车(单车道的叫单线程,多车道的叫多线程)。反正就是一个线程只能处理一件事,处理完后才可以去处理其他事情!
线程有什么用?
线程就是给你的程序后台处理任务的,线程越多,处理的效率就越快(就好比如人多力量大一样),当然,也会消耗更多的内存去运行这些工作的线程!
如何定义单线程?
不需要定义,我们平常事就会用得到!
例如控制台输出helloworld、窗体的操作等等。
做一个简单的例子吧!
我们需要一个窗体应用程序,程序中有一个按钮。
为按钮添加单击事件:里面输出1 到 300000.
如下代码:
private void button1_Click(object sender, EventArgs e) {
text();
}
private void text() {
for (int i = 0; i < 300000; i++) {
Console.WriteLine(i);
}
}
当运行程序,单击按钮后,程序就会在控制台页面输出一到三十万。
在输出的过程中,你的窗体是动不了的,这就出现了窗体“假死状态”,得等到输出完成后,窗体才可以进行其他的操作!(这里就演示不了啦,有兴趣的朋友一定要去试一下)
这是什么原理呢?
其实是我们运行窗体后,就只有一个主线程在进行窗体的运行,当我们点击按钮后,主线程就会紧接着去处理那输出一到三十万那边工作了,进而导致窗体这边没有线程进行运作,所以才出现了“假死”状态!
解决办法就是利用多线程!!!
什么是多线程?
多线程就是两个单线程以上的线程,可以同时处理两个任务!
如何创建多线程?
需要包含命名空间:using System.Threading;
Thread thread = new Thread(text);
text是该线程需要做的工作(一个方法)!
举一个和刚才一样的例子吧!
还是一样的代码,但是这次我们添加了一个线程:
private void button1_Click(object sender, EventArgs e) {
// 创建一个线程去执行这个方法
Thread thread = new Thread(text);
// 标记这个线程准备就绪了,可以随时被执行。
// 具体什么时候执行这个线程,由CPU决定。
thread.Start();
}
private void text() {
for (int i = 0; i < 300000; i++) {
Console.WriteLine(i);
}
}
我们通过Thread去创建一个线程thread,通过它去调用函数text,然后执行Start方法,告诉CPU该线程已经准备完毕,随时等待你的启动!
当我们再次运行时,控制台也还是在输出一到三十万,但是窗体却还可以移动,这就是多线程实现的结果!
我们自己创建的新线程去帮我们执行了text()函数,所以主线程还是留在窗体哪里运作,所以可以进行窗体的其他操作!
这就是多线程的功效!!!
我们讲一个知识点
线程有两种:前台线程和后台线程!
1. 前台线程:只有所有的前台线程都关闭才能完成程序关闭;
2. 后台线程:只有所有的前台线程关闭,后台线程自动关闭。
我们新建的线程默认都是前台线程!
设置为后台线程的方法:
// 设置为后台线程
thread.IsBackground = true;
这点是需要注意一下的!!!
我们再讲例子改变一下需求吧!
这次我们不在控制台输出了,而是改成在窗体添加要给TextBox空间,在这里面显示!
这样着我们看看又会有什么问题呢?
private void button1_Click(object sender, EventArgs e) {
// 创建一个线程去执行这个方法
Thread thread = new Thread(text);
// 标记这个线程准备就绪了,可以随时被执行。
// 具体什么时候执行这个线程,由CPU决定。
thread.Start();
}
private void text() {
for (int i = 0; i < 300000; i++) {
this.textBox1.Text = i.ToString();
}
}
运行截图:
居然报了这样一个错误❌,异常提示太官方化了,我用平常时的华语描述一边:textBox1不是被自己的线程访问,而是被其他线程访问!所以才操作无效。
这也不是不无道理,我们在text函数中使用了textBox1.Text这个控件进行对他自己赋值,但是这个控件又是在主线程创建的,而我们的text函数是在新的线程中调用的,所以就出现了“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”这样的报错!
这是为什么呢?
原因:C# 不允许跨线程调用!
因为我们的程序每次运行时都会进行一次线程的检查,当他检测出来有跨线程调用时,就立即报错了!
解决办法:让程序忽略检查!
我们可以在窗体的Load事件中,加上这句话:
private void Form1_Load(object sender, EventArgs e) {
// 取消跨线程的访问(检查)
Control.CheckForIllegalCrossThreadCalls = false;
}
这样子,就可以正常运行啦!
我们再做一个例子!
这次我们不等它执行完而是直接鼠标点叉×退出窗体,这样子又会抱什么错误呢?
(要等程序执行一会儿后再点叉×,这个报错就会出来了)
这时报了这个不知道什么鬼的错误。。。
原因是:当我们直接点叉×,主线程关闭了,我们的新线程还没有关闭还在运转,所以就发生了报错!
解决办法就是:当我们点击叉×的那一瞬间,检查新线程是否已经关闭,如果没有关闭,则需要人工关闭!
所以我们得为窗体绑定一个FormClosing事件,在这个事件中我们需要自己手动去关闭新的线程!
使用Abort()函数即可!
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// 主线程关闭后,新线程不为NULL则需手动关闭线程
if (thread != null) {
thread.Abort();
}
}
好了,到了这里线程的知识点也都讲得差不多了,我们下面来做一个线程的小项目吧!
摇奖机
界面如下:
当我们点击开始按钮后,按钮的文本会变成停止,然后上面三个leible会刷新随机数,直到我们点停止为止!
效果如下:
我这个我就不细讲了,把代码写下来有兴趣的可以看一看:
*******摇奖机*********/
// 主要是控制随机数
bool isBool = false;
private void btnStart_Click(object sender, EventArgs e) {
if (isBool == false) {
isBool = true;
this.btnStart.Text = "停止";
Thread th = new Thread(Randoms); // 创建一个线程
th.IsBackground = true; // 设置为后台线程
th.Start(); // 启动线程
} else {
isBool = false;
this.btnStart.Text = "开始";
}
}
private void Randoms() {
Random random = new Random(); // 实例化随机对象
// 该循环由按钮控制
while (isBool) {
// 随机产生 0 - 9
this.label1.Text = random.Next(0, 9).ToString();
this.label2.Text = random.Next(0, 9).ToString();
this.label3.Text = random.Next(0, 9).ToString();
}
}
下面是全部代码集合:
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 线程学习 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
// 线程
Thread thread;
private void button1_Click(object sender, EventArgs e) {
// 创建一个线程去执行这个方法
thread = new Thread(text);
// 设置为后台线程
thread.IsBackground = true;
// 标记这个线程准备就绪了,可以随时被执行。
// 具体什么时候执行这个线程,由CPU决定。
thread.Start();
}
private void text() {
for (int i = 0; i < 300000; i++) {
//Console.WriteLine(i);
this.textBox1.Text = i.ToString();
}
}
private void Form1_Load(object sender, EventArgs e) {
// 取消跨线程的访问(检查)
Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// 主线程关闭后,新线程不为NULL则需手动关闭线程
if (thread != null) {
thread.Abort();
}
}
/*******摇奖机*********/
// 主要是控制随机数
bool isBool = false;
private void btnStart_Click(object sender, EventArgs e) {
if (isBool == false) {
isBool = true;
this.btnStart.Text = "停止";
Thread th = new Thread(Randoms); // 创建一个线程
th.IsBackground = true; // 设置为后台线程
th.Start(); // 启动线程
} else {
isBool = false;
this.btnStart.Text = "开始";
}
}
private void Randoms() {
Random random = new Random(); // 实例化随机对象
// 该循环由按钮控制
while (isBool) {
// 随机产生 0 - 9
this.label1.Text = random.Next(0, 9).ToString();
this.label2.Text = random.Next(0, 9).ToString();
this.label3.Text = random.Next(0, 9).ToString();
}
}
}
}
总结:
好了,线程的知识点我懂的也就这些了,说好的简介,也还是写下了那么多,也就这样吧,希望对看到这篇博客的朋友有帮助吧!