- 两个名词解释:进程和线程
进程:简单说是指在系统中正在运行的一个应用程序。在windows系统中,通过资源管理器可以看到每个应用进程。
线程:是系统分配处理器时间资源的基本单元。一个进程里包含多个进程。
- 多线程作用
执行比较耗时任务时,也同时可以解决一些非常耗时的程序长时间占用CPU资源。比如:迅雷下载影片时,可以一边下着,一边观看其他影片,这就是开了多线程
- 多线程特点
运行顺序不确定;线程之间并行(其实是交替执行)
初步演示代码如下:
/*
* 作者:WangYadong
*/
using System;
using System.Threading;
namespace 学习多线程
{
class Demo1
{
//无参方法
private void ThreadMethod_1()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程1");
}
}
//无参方法
private void ThreadMethod_2()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程2");
}
}
private void Test1()
{
Thread th1 = new Thread(ThreadMethod_1);
th1.Start();
Thread th2 = new Thread(ThreadMethod_2);
th2.Start();
}
static void Main(string[] args)
{
Demo1 obj = new Demo1();
obj.Test1();
}
}
}
新建两个方法ThreadMethod_1()和ThreadMethod_2(),然后在Test1()中开两个线程th1和th2,分别调用这两个方法
我们可以看到这两个方法被以参数的形式传递给了新起线程的构造函数中(本质还是委托),然后再用Start()启用该线程
运行结果如下:
可以看到两个线程的输出结果是交替的
- 开启带参线程
方法一(此方式只能传递一个参数):
//带参方法
private void ThreadMethod_3(object paraName)
{
Console.WriteLine("我是线程3: " + Thread.CurrentThread.ManagedThreadId + " 参数为: " + paraName.ToString());
}
调用如下:
/// <summary>
/// 此种方式只能传递一个参数
/// </summary>
private void Test2()
{Thread th3 = new Thread(ThreadMethod_3);
th3.Start("AAA"); //参数在这里传递
}
方法二(使用“类”进行传参):
新建一个MyThread类:
/***************
*
* Title:
*
* Description:
* 功能:
*
* Author:王亚东
*
* Date:2024
*
* Modify:
*
* ****/
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace 学习多线程
{
class MyThread
{
private string _FilePath;
private string _FileName;
public MyThread(string filePath,string fileName)
{
_FilePath = filePath;
_FileName = fileName;
}
public void DownFileLoad()
{
Console.WriteLine("开始下载: " + _FilePath + "/" + _FileName);
Thread.Sleep(2);
Console.WriteLine("下载完毕!");
}
}
}
调用如下:
/// <summary>
/// 传递多个参数--使用类进行传参
/// </summary>
private void Test3()
{
MyThread myThreadObj = new MyThread("D:/File", "abc.mp4");
Thread th = new Thread(myThreadObj.DownFileLoad);
th.Start();
}
- 委托开启线程 (使用委托开启的线程都是后台线程)
1、委托开启线程,调无参方法:
//无参方法
private void ThreadMethod_1()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程1");
}
}
调用如下:
/// <summary>
/// 委托开启线程(无参)
/// </summary>
private void Test4()
{
Action ac = ThreadMethod_1;
ac.BeginInvoke(null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
Console.ReadLine(); //这里必须要加这句代码,否则该委托线程不执行,因为委托开启的是后台线程,Main是主线程,是前台线程,前台线程瞬间执行完后,后台线程还没来得及执行
}
2、委托开启线程 ,调有参方法:
//带参方法
private void ThreadMethod_4(int num)
{
Console.WriteLine("我是线程3: " + Thread.CurrentThread.ManagedThreadId + " 参数为: " + num);
}
调用如下:
/// <summary>
/// 委托开启线程(带参)
/// </summary>
private void Test5()
{
Action<int> ac = ThreadMethod_4;
ac.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
Console.ReadLine(); //这里必须要加这句代码,否则该委托线程不执行,因为委托开启的是后台线程,Main是主线程,是前台线程,前台线程瞬间执行完后,后台线程还没来得及执行
}
3、委托开启线程,使用“状态结果”,来等待子线程结束,接收子线程的结果:
private int DownFileMoreFile(int num)
{
Console.WriteLine("下载很多文件");
Thread.Sleep(3000);
return num + 2000;
}
调用如下:
/// <summary>
/// 委托开启线程,传参,有返回值
/// </summary>
private void Test6()
{
Func<int, int> fu = DownFileMoreFile;
IAsyncResult result = fu.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
while (!result.IsCompleted)
{
Console.WriteLine(".");
}
int endResult = fu.EndInvoke(result);
Console.WriteLine("线程结束,返回值为:" + endResult);
//Console.ReadLine(); //这里可以不用加这行代码,因为while循环已经在执行了
}
4、委托开启线程,使用“等待句柄”,来等待子线程结束,接收子线程的结果:
private int DownFileMoreFile(int num)
{
Console.WriteLine("下载很多文件");
Thread.Sleep(3000);
return num + 2000;
}
调用如下:
/// <summary>
/// 委托开启线程,传参,有返回值,用等待句柄的方式代替上个方法中的while循环(二者效果一样)
/// </summary>
private void Test7()
{
Func<int, int> fu = DownFileMoreFile;
IAsyncResult result = fu.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
bool boResult = result.AsyncWaitHandle.WaitOne(1000); //用“等待句柄”,等待1秒钟,且有状态返回值
if (boResult)
{
int endResult = fu.EndInvoke(result);
Console.WriteLine("线程结束,返回值为:" + endResult);
}
else
{
Console.Write("规定时间内,没有取得子线程的结果。"); //会执行else,不会再执行if,因为DownFileMoreFile方法里是等待3秒,而这里只等待1秒,所以无法取得结果
}
//Console.ReadLine(); //这里可以不用加这行代码,因为等待句柄已经在执行了
}
5、委托开启线程,使用“回调函数”,来接收子线程的结果
private void Test8()
{
Console.WriteLine("使用‘回调函数’方式");
Func<int, int> fu = DownFileMoreFile;
//当子线程结束的时候,自动调用回调函数OnCallBack
fu.BeginInvoke(100, OnCallBack, fu); //这里的 fu 参数是传递给回调函数中的ar参数
Console.ReadLine(); //这里必须加这行代码,因为子线程中是等待3秒,而主线程(即前台线程)瞬间执行完毕
}void OnCallBack(IAsyncResult ar)
{
Func<int, int> res = ar.AsyncState as Func<int, int>;
int intRessult = res.EndInvoke(ar);
Console.WriteLine("在回调函数中取得结果");
}注:在Main函数中调用Test8方法即可
- 线程优先级
/// <summary>
/// 线程优先级学习
/// </summary>
private void Test9()
{
Thread th1 = new Thread(ThreadMethod_1);
Thread th2 = new Thread(ThreadMethod_2);
Thread th3 = new Thread(ThreadMethod_2_1);//总共有5种优先级状态,线程的优先级决定了它应该在操作系统中被分配多少CPU时间。
//高优先级的线程可能会在低优先级的线程之前执行。但是,请注意,设置线程的优先级并不保证它会获得更多的CPU时间。这完全取决于操作系统的调度策略。
th1.Priority = ThreadPriority.Lowest; //线程共有5个优先级
th2.Priority = ThreadPriority.AboveNormal;
th3.Priority = ThreadPriority.Highest;th1.Start();
th2.Start();
th3.Start();
}
- 线程状态
线程共有4中状态:Start()//开始 Abort() //结束 Join(1000)//阻塞 Sleep(1000)//休眠
Join和Sleep比较相似
代码示例如下:
private void Test10()
{
Thread t1 = new Thread(ThreadMethod_1);
t1.Start(); //开始
t1.Abort(); //结束
t1.Join(1000); //阻塞1秒
Thread.Sleep(1000); //休眠1秒}
- 前台线程与后台线程
前台与后台线程区别:
前台线程:重要的,核心的,或者需要长时间等待的任务(例如:UI界面线程、发送数据的线程)
后台线程:非常适合于完成后台任务,非核心且用于处理时间较短的任务使用(例如:监听客户端的请求、word拼写检查)
* Thread构造的线程默认是前台线程,将isBackground设置为true后就是后台线程
* 线程池中的线程是后台线程
* 后台线程随着前台线程的关闭而关闭
* 用委托开启的线程都是后台线程
private void Test11()
{
Thread t1 = new Thread(ThreadMethod_3);
t1.Start("abc");
//............在这之前t1还是前台线程
t1.IsBackground = true; //将线程设置为后台线程,加上这一行代码后,ThreadMethod_3方法就不执行,因为设置为后台线程后,随着前台线程结束而瞬间结束
Console.ReadLine(); //加上这句代码,ThreadMethod_3方法就又执行了,因为前台线程未结束}
完整代码如下:
/*
* 作者:WangYadong
* 1、了解无参线程,及线程特性(无序)
* 2、带参方法,开线程
* 3、使用类传递多参数线程
* 4、委托开启线程
* 注意:委托开启的线程都是后台线程。“后台”线程会随着“前台”线程的关闭而自动关闭
* 4.1:使用“状态结果”,来等待子线程结束,接收子线程的结果
* 4.2:使用“等待句柄”,来等待子线程结束,接收子线程的结果
* 4.3:使用“回调函数”,来接收子线程的结果
* 5:学习线程优先级
* 6:学习线程的状态控制
* 7:学习前台线程与后台线程
* 7.1:Thread构造的线程默认是前台线程,将isBackground设置为true后就是后台线程
* 7.2:线程池中的线程是后台线程
* 7.3:后台线程随着前台线程的关闭而关闭
* 7.4:用委托开启的线程都是后台线程
* 前台线程:重要的,核心的,或者需要长时间等待的任务(例如:UI界面线程、发送数据的线程)
* 后台线程:非常适合于完成后台任务,非核心且用于处理时间较短的任务使用(例如:监听客户端的请求、word拼写检查)
*
*/
using System;
using System.Threading;
namespace 学习多线程
{
class Demo1
{
//无参方法
private void ThreadMethod_1()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程1");
}
}
//无参方法
private void ThreadMethod_2()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程2");
}
}
private void ThreadMethod_2_1()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("我是线程2_1");
}
}
//带参方法
private void ThreadMethod_3(object paraName)
{
Console.WriteLine("我是线程3: " + Thread.CurrentThread.ManagedThreadId + " 参数为: " + paraName.ToString());
}
//带参方法
private void ThreadMethod_4(int num)
{
Console.WriteLine("我是线程3: " + Thread.CurrentThread.ManagedThreadId + " 参数为: " + num);
}
private int DownFileMoreFile(int num)
{
Console.WriteLine("下载很多文件");
Thread.Sleep(3000);
return num + 2000;
}
private void Test1()
{
Thread th1 = new Thread(ThreadMethod_1);
th1.Start();
Thread th2 = new Thread(ThreadMethod_2);
th2.Start();
}
/// <summary>
/// 此种方式只能传递一个参数
/// </summary>
private void Test2()
{
Thread th3 = new Thread(ThreadMethod_3);
th3.Start("AAA"); //参数在这里传递
}
/// <summary>
/// 传递多个参数--使用类进行传参
/// </summary>
private void Test3()
{
MyThread myThreadObj = new MyThread("D:/File", "abc.mp4");
Thread th = new Thread(myThreadObj.DownFileLoad);
th.Start();
}
/// <summary>
/// 委托开启线程(无参)
/// </summary>
private void Test4()
{
Action ac = ThreadMethod_1;
ac.BeginInvoke(null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
Console.ReadLine(); //这里必须要加这句代码,否则该委托线程不执行,因为委托开启的是后台线程,Main是主线程,是前台线程,前台线程瞬间执行完后,后台线程还没来得及执行
}
/// <summary>
/// 委托开启线程(带参)
/// </summary>
private void Test5()
{
Action<int> ac = ThreadMethod_4;
ac.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
Console.ReadLine(); //这里必须要加这句代码,否则该委托线程不执行,因为委托开启的是后台线程,Main是主线程,是前台线程,前台线程瞬间执行完后,后台线程还没来得及执行
}
/// <summary>
/// 委托开启线程,传参,有返回值
/// </summary>
private void Test6()
{
Func<int, int> fu = DownFileMoreFile;
IAsyncResult result = fu.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
while (!result.IsCompleted)
{
Console.WriteLine(".");
}
int endResult = fu.EndInvoke(result);
Console.WriteLine("线程结束,返回值为:" + endResult);
//Console.ReadLine(); //这里可以不用加这行代码,因为while循环已经在执行了
}
/// <summary>
/// 委托开启线程,传参,有返回值,用等待句柄的方式代替上个方法中的while循环(二者效果一样)
/// </summary>
private void Test7()
{
Func<int, int> fu = DownFileMoreFile;
IAsyncResult result = fu.BeginInvoke(100, null, null); //新版本的VS更新后不再支持这个委托方法了,所以运行报错
bool boResult = result.AsyncWaitHandle.WaitOne(1000); //用“等待句柄”,等待1秒钟,且有状态返回值
if (boResult)
{
int endResult = fu.EndInvoke(result);
Console.WriteLine("线程结束,返回值为:" + endResult);
}
else
{
Console.Write("规定时间内,没有取得子线程的结果。"); //会执行else,不会再执行if,因为DownFileMoreFile方法里是等待3秒,而这里只等待1秒,所以无法取得结果
}
//Console.ReadLine(); //这里可以不用加这行代码,因为等待句柄已经在执行了
}
/// <summary>
/// 委托开启线程,传参,有返回值,用“回调函数”获取返回结果
/// </summary>
private void Test8()
{
Console.WriteLine("使用‘回调函数’方式");
Func<int, int> fu = DownFileMoreFile;
//当子线程结束的时候,自动调用回调函数OnCallBack
fu.BeginInvoke(100, OnCallBack, fu); //这里的 fu 参数是传递给回调函数中的ar参数
Console.ReadLine(); //这里必须加这行代码,因为子线程中是等待3秒,而主线程(即前台线程)瞬间执行完毕
}
void OnCallBack(IAsyncResult ar)
{
Func<int, int> res = ar.AsyncState as Func<int, int>;
int intRessult = res.EndInvoke(ar);
Console.WriteLine("在回调函数中取得结果");
}
/// <summary>
/// 线程优先级学习
/// </summary>
private void Test9()
{
Thread th1 = new Thread(ThreadMethod_1);
Thread th2 = new Thread(ThreadMethod_2);
Thread th3 = new Thread(ThreadMethod_2_1);
//总共有5种优先级状态,线程的优先级决定了它应该在操作系统中被分配多少CPU时间。
//高优先级的线程可能会在低优先级的线程之前执行。但是,请注意,设置线程的优先级并不保证它会获得更多的CPU时间。这完全取决于操作系统的调度策略。
th1.Priority = ThreadPriority.Lowest;
th2.Priority = ThreadPriority.AboveNormal;
th3.Priority = ThreadPriority.Highest;
th1.Start();
th2.Start();
th3.Start();
}
private void Test10()
{
Thread t1 = new Thread(ThreadMethod_1);
t1.Start(); //开始
t1.Abort(); //结束
t1.Join(1000); //阻塞1秒
Thread.Sleep(1000); //休眠1秒
}
private void Test11()
{
Thread t1 = new Thread(ThreadMethod_3);
t1.Start("abc");
//............在这之前t1还是前台线程
t1.IsBackground = true; //将线程设置为后台线程,加上这一行代码后,ThreadMethod_3方法就不执行,因为设置为后台线程后,随着前台线程结束而瞬间结束
Console.ReadLine(); //加上这句代码,ThreadMethod_3方法就又执行了,因为前台线程未结束
}
static void Main(string[] args)
{
Demo1 obj = new Demo1();
obj.Test11();
}
}
}