C#中的各种多线程使用场景的demo,便于快速对号入座使用
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyThreadDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//同步异步:
#region MyRegion
//1. 进程/线程/多线程
//2. 同步异步
//3. 异步的三大特点
//4. C#中的多线程
//一、进程:计算机概念,虚拟的记录,描述一个应用程序在计算机上运行的时候,所消耗的各种资源的集合---Cpu+内容+磁盘IO+网络资源;
//---类似于某一个公司---公司正常运作;
//二、线程:(计算机资源)一个程序的进程中,所执行的具体的某一个动作的最小执行流;小到点击某个按钮,大到通过网络发送一句话出去;
//具体到某一个人:
//进程必然是包含了多个线程;线程是依附于进程存在的;如果进程不在了,线程也随之消失了;
//三、句柄:是一个long了类型的数字,对应这计算机程序中的某一个小部件;要操作程序,对应的最小的单位;
//公司中的任何一个物件--编号--计算机---会给计算机坐个编号---(部门---座位号)
//四、为什么计算机可以多线程?跟计算机的CPU的核数有关;
//6核12线程:把六个核心进行逻辑切分:如果有动作需要计算机响应,操作系统就会去申请CPU来处理;CPU在执行动作的时候,CPU是分片执行:
//分片:把每一个核每一毫秒可以执行的动作,切分成10000份;操作系统在调度的时候,执行动作,切分后去执行的多个动作的时候;开始动作A,使用的是分片后的某一份; 结束的时候,可能使用的是另外的某一份;
//计算机在不断的调度过程中;
//1.从宏观角度来说:多个动作,就可以做到在某一段时间内;看似同时执行完毕了;
//2.从微观的角度来说:某一时刻,同意时刻只能处理一件事儿--串行执行;
//五、C#中的多线程:Thread/ThreadPool/Task 都是C#语言在操作计算机的资源(线程)时;封装的一个帮助类库;
//线程:---同步异步:
//单线程执行代码命令:同步执行;
//同步方法:
//1.线性执行,从上往下依次执行--很符合我们人类的思维逻辑,线程ID为01 (主线程/UI线程)
// 请人吃饭:真心的请人吃饭;Richard老师请Vn吃饭;说:Vn,晚上一起去吃饭;Vn:我还要忙一会儿,Richard:那好,我等你;等你忙完,我们一起去吃饭;
//2.同步方法执行慢;卡顿界面--只有一个线程参与计算---CPU太忙,根本无暇他顾;只有一个线程执行动作; 消耗的计算机资源少(工钱少)
//3.同步方法:有序执行;
//多线程执行代码命令:异步执行:
//异步方法:
//1.开启了一个新的线程(多线程--子线程)(有一个新的线程Id);且不再是线型执行了; UI线程不等待子线程执行完毕;主线程直接往后执行; 子线程延迟执行
//请人吃饭:客气一下请人吃饭:Richard老师客气一下请人Vn吃饭,说:Vn,晚上一起吃饭,Vn:我还要忙一会儿,Richard:那你先忙吧,我就先去吃饭了,你忙完自己去吃饭吧!
//2.异步方法执行快:不卡顿界面;开启了一个新的线程去执行去了;开启了五个线程去执行动作:消耗的计算机资源多(工钱多)
// 异步方法--多线程方法--以资源换时间; 使用大量的资源,降低时间成本;
//3.无序执行:线程开启无法控制谁先谁后;线程执行计算结束也无法控制谁先谁后;---多线程异步执行:无序执行;
#endregion
//Thread
//ThreadPool
//Task
/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
for (int i = 0; i < 5; i++)
{
DoSomething("kulala");
}
Debug.WriteLine($"****************btnSync_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsnyc_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
#region 委托异步调用
//在ASP.NET Core的年代:BeginInvoke不再支持了;如果想要看看这个,可以参考第14期;
//Action<string> action = this.DoSomething;
//AsyncCallback asyncCallback = null;
//action.BeginInvoke(" Richard ", asyncCallback, "Object");
#endregion
//Task开启了一个线程
for (int i = 0; i < 5; i++)
{
Task.Run(() =>
{
DoSomething("kulala");
});
}
Debug.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#region Private Method
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomething(string name)
{
Debug.WriteLine($"****************DoSomething Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000_000_000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Debug.WriteLine($"****************DoSomething End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyThreadDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//同步异步:
#region MyRegion
//1. 进程/线程/多线程
//2. 同步异步
//3. 异步的三大特点
//4. C#中的多线程
//一、进程:计算机概念,虚拟的记录,描述一个应用程序在计算机上运行的时候,所消耗的各种资源的集合---Cpu+内容+磁盘IO+网络资源;
//---类似于某一个公司---公司正常运作;
//二、线程:(计算机资源)一个程序的进程中,所执行的具体的某一个动作的最小执行流;小到点击某个按钮,大到通过网络发送一句话出去;
//具体到某一个人:
//进程必然是包含了多个线程;线程是依附于进程存在的;如果进程不在了,线程也随之消失了;
//三、句柄:是一个long了类型的数字,对应这计算机程序中的某一个小部件;要操作程序,对应的最小的单位;
//公司中的任何一个物件--编号--计算机---会给计算机坐个编号---(部门---座位号)
//四、为什么计算机可以多线程?跟计算机的CPU的核数有关;
//6核12线程:把六个核心进行逻辑切分:如果有动作需要计算机响应,操作系统就会去申请CPU来处理;CPU在执行动作的时候,CPU是分片执行:
//分片:把每一个核每一毫秒可以执行的动作,切分成10000份;操作系统在调度的时候,执行动作,切分后去执行的多个动作的时候;开始动作A,使用的是分片后的某一份; 结束的时候,可能使用的是另外的某一份;
//计算机在不断的调度过程中;
//1.从宏观角度来说:多个动作,就可以做到在某一段时间内;看似同时执行完毕了;
//2.从微观的角度来说:某一时刻,同意时刻只能处理一件事儿--串行执行;
//五、C#中的多线程:Thread/ThreadPool/Task 都是C#语言在操作计算机的资源(线程)时;封装的一个帮助类库;
//线程:---同步异步:
//单线程执行代码命令:同步执行;
//同步方法:
//1.线性执行,从上往下依次执行--很符合我们人类的思维逻辑,线程ID为01 (主线程/UI线程)
// 请人吃饭:真心的请人吃饭;Richard老师请Vn吃饭;说:Vn,晚上一起去吃饭;Vn:我还要忙一会儿,Richard:那好,我等你;等你忙完,我们一起去吃饭;
//2.同步方法执行慢;卡顿界面--只有一个线程参与计算---CPU太忙,根本无暇他顾;只有一个线程执行动作; 消耗的计算机资源少(工钱少)
//3.同步方法:有序执行;
//多线程执行代码命令:异步执行:
//异步方法:
//1.开启了一个新的线程(多线程--子线程)(有一个新的线程Id);且不再是线型执行了; UI线程不等待子线程执行完毕;主线程直接往后执行; 子线程延迟执行
//请人吃饭:客气一下请人吃饭:Richard老师客气一下请人Vn吃饭,说:Vn,晚上一起吃饭,Vn:我还要忙一会儿,Richard:那你先忙吧,我就先去吃饭了,你忙完自己去吃饭吧!
//2.异步方法执行快:不卡顿界面;开启了一个新的线程去执行去了;开启了五个线程去执行动作:消耗的计算机资源多(工钱多)
// 异步方法--多线程方法--以资源换时间; 使用大量的资源,降低时间成本;
//3.无序执行:线程开启无法控制谁先谁后;线程执行计算结束也无法控制谁先谁后;---多线程异步执行:无序执行;
#endregion
//Thread
//ThreadPool
//Task
/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
for (int i = 0; i < 5; i++)
{
DoSomething("kulala");
}
Debug.WriteLine($"****************btnSync_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsnyc_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
#region 委托异步调用
//在ASP.NET Core的年代:BeginInvoke不再支持了;如果想要看看这个,可以参考第14期;
//Action<string> action = this.DoSomething;
//AsyncCallback asyncCallback = null;
//action.BeginInvoke(" Richard ", asyncCallback, "Object");
#endregion
//Task开启了一个线程
for (int i = 0; i < 5; i++)
{
Task.Run(() =>
{
DoSomething("kulala");
});
}
Debug.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#region Private Method
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomething(string name)
{
Debug.WriteLine($"****************DoSomething Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000_000_000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Debug.WriteLine($"****************DoSomething End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
/// <summary>
/// Task:
///
///在最老版本中,有Thread/ThreadPool
///在前几天,在QQ大群有相关的公告的,你们下载观看
///1..NetFramework3.0时代实现
///2.C#中多线程操作的最佳实现---丰富的Api,我们在开发中遇到个各种场景,他都可以满足到;3.Task操作的线程来自于线程/池
///Task,Prallel他们所操作的线程都是来自于线程池;
///
///确定线程的标识:线程Id,线程Id不一样,线程其实就是不同的线程;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTask_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnTask_Click Start|| 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//一、Task开启线程有哪些方式:
#region MyRegion
//多线程:他的应用场景:
//一、在什么情况下,可以使用多线程?
//1.一个数据库查询:---可以使用多线程吗?不适合使用多线程;因为只有一个动作;不存在多个动作并发执行;
//2.如果要同时去查询接口+查询缓存+查询数据库?--适合使用多线程吗?---可以开启三个线程;每个线程就负责一个动作;
//通过一个案例,来解析:
//二、高级班VIP课程讲课:
//1.开始上课---Richard老师开始讲课--一节一节的开始讲:适合使用多线程吗?--不适合:不能同时讲多个课
//。。。。。经过大概8个月的时间
//2.开始项目实战---分组进行;多人合作完成; 适合使用多线程吗? ---适合;每个人就可以对应一个线程;多个人同时去开发功能---多线程并发;
//3.如果大家其中有一个人完成了功能的开发,Richard老师就要开始准备环境了。
//a.需要等待一堆线程中的某一个线程执行结束,马上触发一个动作;
//解决方案: Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待---会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
//4.如果所有人开发完毕了,庆祝一下,一起吃个饭;
//三、应用场景:
//1.ContinueWhenAny
//如果需要在一堆任务中,某一种执行结束后,去触发一个动作;
//2.ContinueWhenAll
//如果需要回调---如果需要控制顺序:
//3.WaitAny
// 有一个列表数据:有可能是来自于第三方接口,有可能是来自于缓存,也有可能是来自于数据库,查询的时候,我们不确定;
// 常规玩法: 先找缓存,如果有数据,直接返回了,如果缓存没有--可能要去查询数据库,如果数据库有数据,直接返回,如果没有---查询第三方接口;
// 多线程:同时开启三个线程,同时去查询三个地方的数据,只要是有一个地方的数据查询到了,只要是有一个地方的数据找到了,其他的两个线程我们就不管了;直接把找到的数据返回给前端就可以了;
//
//
//4.WaitAll
// 如果有一个首页,首页中包含了很多功能:考勤信息,年度top10,季度top10,公告,通知;
// 在主页中:要加载主页-这些信息都需要查询出来: 而且这些信息都是来自于不同的地方,有的来自于接口,有的来自于缓存有的都来于数据库:
//有几个块信息,就开启几个线程,同时分别去各自的地方进行查询各自的数据;可以在后台等待所有的子线程执行结束;拿到结果、组件一个大的复杂实体对象,传递给视图,通过视图做绑定;--让查询并发执行: 但是这里如果是使用C#开发,不做前后端分离的话,可以考虑这种,可以提高性能;
//现在更多的时候,可能是使用异步Ajax来完成---用户体验会更好; 资源会有所浪费,但是性能可以提高;
#endregion
//父子级线程
//一个Task内部,开启了几个线程以后;Task内部的线程可以理解为子线程;
//父线程在等待的时候,子线程没有线程等待;子线程可能内容还没有执行万一,父线程就已经结束了;
{
Task task = new Task(() =>
{
Debug.WriteLine($"****************task开始了: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Task task1 = new Task(() =>
{
Debug.WriteLine($"****************task1: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Thread.Sleep(1000);
Debug.WriteLine("我是task1线程");
});
Task task2 = new Task(() =>
{
Debug.WriteLine($"****************task2: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Thread.Sleep(1000);
Debug.WriteLine("我是task2线程");
});
task1.Start();
task2.Start();
//task1.Wait();
//task2.Wait();
Debug.WriteLine($"****************task: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
});
task.Start();//线程启动了
task.Wait(); //单个线程的等待;等待task 内部的内容执行完毕,这里会卡顿界面;是主线程等待;
// task.Wait();//等待执行完成:
task.Wait(TimeSpan.FromMilliseconds(1100));
task.Wait(1000);//限时等待;过时不候
// Thread thread = null;
//thread.Abort();//线程停止;
//thread.Start();
//thread.IsBackground = true;//后台线程:进程结束,线程随之消失
//thread.IsBackground = false;//前台线程:进程结束,线程把内部需要执行的动作执行完毕,然后消失
}
//TaskCreationOptions.PreferFairness 相对来说比较公平执行的:如果是先申请的线程,就会优先执行;
{
//Task task1= new Task(() =>
//{
//}, TaskCreationOptions.PreferFairness);
//Task task2 = new Task(() =>
//{
//}, TaskCreationOptions.PreferFairness);
}
{
// {
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// Thread.Sleep(3000);//主线程执行到这儿会卡顿等待;等待3000ms后继续往后;
// stopwatch.Stop();
// Debug.WriteLine($"Thread.Sleep(3000):{stopwatch.ElapsedMilliseconds}");
// }
// {
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// //回调:Delay一般和ContinueWith配合使用;
// //不卡顿界面:3000ms以后,去执行一段业务逻辑:执行的动作就是ContinueWith内部的委托;ContinueWith内部的执行有可能是一个全新的线程去执行,也有可能是主线程去执行;
// Task.Delay(3000).ContinueWith(t =>
// {
// Debug.WriteLine($"****************Delay || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
// stopwatch.Stop();
// Debug.WriteLine($"Task.Delay(3000):{stopwatch.ElapsedMilliseconds}");
// });//一般情况下,这个Delay 是等待3000ms继续做点什么---或者说延迟3000ms后做点什么;
// }
}
//现在是20:56,大家休息5分钟&&上厕所,21:01继续~~
//
//多线程:他的应用场景:
//一、在什么情况下,可以使用多线程?
//1.一个数据库查询:---可以使用多线程吗?不适合使用多线程;因为只有一个动作;不存在多个动作并发执行;
//2.如果要同时去查询接口+查询缓存+查询数据库?--适合使用多线程吗?---可以开启三个线程;每个线程就负责一个动作;
//通过一个案例,来解析:
//二、高级班VIP课程讲课:
//1.开始上课---Richard老师开始讲课--一节一节的开始讲:适合使用多线程吗?--不适合:不能同时讲多个课
//。。。。。经过大概8个月的时间
//2.开始项目实战---分组进行;多人合作完成; 适合使用多线程吗? ---适合;每个人就可以对应一个线程;多个人同时去开发功能---多线程并发;
//3.如果大家其中有一个人完成了功能的开发,Richard老师就要开始准备环境了。
//a.需要等待一堆线程中的某一个线程执行结束,马上触发一个动作;
//解决方案: Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待---会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
//4.如果所有人开发完毕了,庆祝一下,一起吃个饭;
//三、应用场景:
//1.ContinueWhenAny
//如果需要在一堆任务中,某一种执行结束后,去触发一个动作;
//2.ContinueWhenAll
//如果需要回调---如果需要控制顺序:
//3.WaitAny
// 有一个列表数据:有可能是来自于第三方接口,有可能是来自于缓存,也有可能是来自于数据库,查询的时候,我们不确定;
// 常规玩法: 先找缓存,如果有数据,直接返回了,如果缓存没有--可能要去查询数据库,如果数据库有数据,直接返回,如果没有---查询第三方接口;
// 多线程:同时开启三个线程,同时去查询三个地方的数据,只要是有一个地方的数据查询到了,只要是有一个地方的数据找到了,其他的两个线程我们就不管了;直接把找到的数据返回给前端就可以了;
//
//
//4.WaitAll
// 如果有一个首页,首页中包含了很多功能:考勤信息,年度top10,季度top10,公告,通知;
// 在主页中:要加载主页-这些信息都需要查询出来: 而且这些信息都是来自于不同的地方,有的来自于接口,有的来自于缓存有的都来于数据库:
//有几个块信息,就开启几个线程,同时分别去各自的地方进行查询各自的数据;可以在后台等待所有的子线程执行结束;拿到结果、组件一个大的复杂实体对象,传递给视图,通过视图做绑定;--让查询并发执行: 但是这里如果是使用C#开发,不做前后端分离的话,可以考虑这种,可以提高性能;
//现在更多的时候,可能是使用异步Ajax来完成---用户体验会更好; 资源会有所浪费,但是性能可以提高;
{
Debug.WriteLine("第15期高级班课程起飞。。。");
Debug.WriteLine("Richard老师开始讲课了。。。。");
this.Teach("跨平台开发.NET5");
this.Teach("高并发组件Redis/Nginx/RabbitMQ/Memcache/Mongo...");
this.Teach("各种框架扩展定制。。。。");
Debug.WriteLine("Richard老师知识点已经讲完了。。。。。");
Debug.WriteLine("开始项目实战----直播平台实战课");
List<Task> taskList = new List<Task>();
TaskFactory factory = new TaskFactory();
taskList.Add(factory.StartNew(obj => Coding("嘟嘟", "权限的数据库设计"), "嘟嘟"));
taskList.Add(factory.StartNew(obj => Coding("年轻人不讲武德", "WeChat接口对接"), "年轻人不讲武德"));
taskList.Add(factory.StartNew(obj => Coding("闻", "脚手架框架搭建"), "闻"));
taskList.Add(factory.StartNew(obj => Coding("超越", "ABPVNetxt框架搭建"), "超越"));
taskList.Add(factory.StartNew(obj => Coding("角印", "编写Webapi"), "角印"));
{
//相当于是一个回调;主线程执行的时候,这里是不卡段界面的;当taskList集合中的某一个线程执行结束语了;就触发后面委托中的动作;---不卡顿界面--用户体验就更好
factory.ContinueWhenAny(taskList.ToArray(), ts =>
{
Debug.WriteLine($"{ts.AsyncState}同学开发完毕,Richard老师开始准备环境。。。");
});
//
}
{
factory.ContinueWhenAll(taskList.ToArray(), ts =>
{
Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
});
}
{
//Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待-- - 会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
Debug.WriteLine("xxxx同学开发完毕,Richard老师开始准备环境。。。");
}
{
//Task.WaitAll--等待几个线程都执行结束;---主线程会等待-- 一直到所有的线程执行结束,才往后执行;- 会卡顿界面;--体验不好
Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
Task.WaitAll(taskList.ToArray());
Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
}
List<Action> actions = new List<Action>();
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 2; //下面在开启线程执行委托的时候,最多开启2个线程;
Parallel.Invoke(options,actions.ToArray()); //会开启四个线程分别去执行;
//如果想要限制线程数量
}
Debug.WriteLine($"****************btnTask_Click End || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
//现在是22:06,大家就开始提问&&休息,22:10,开始答疑!期间老师不说话了
#region Private Method
/// <summary>
/// 模拟了讲课的过程:
/// </summary>
/// <param name="lesson"></param>
private void Teach(string lesson)
{
Debug.WriteLine($"{lesson} ||开始了。。。");
long lResult = 0;
//for (int i = 0; i < 1_000_000_000; i++)
//{
// lResult += i;
//}
Debug.WriteLine($"{lesson} || 讲完了。。。");
}
/// <summary>
/// 模拟Coding过程
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name, string projectName)
{
Debug.WriteLine($"****************Coding Start || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1_000_000_000; i++)
{
lResult += i;
}
Thread.Sleep(3000);
Debug.WriteLine($"****************Coding End || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyThreadDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//同步异步:
#region MyRegion
//1. 进程/线程/多线程
//2. 同步异步
//3. 异步的三大特点
//4. C#中的多线程
//一、进程:计算机概念,虚拟的记录,描述一个应用程序在计算机上运行的时候,所消耗的各种资源的集合---Cpu+内容+磁盘IO+网络资源;
//---类似于某一个公司---公司正常运作;
//二、线程:(计算机资源)一个程序的进程中,所执行的具体的某一个动作的最小执行流;小到点击某个按钮,大到通过网络发送一句话出去;
//具体到某一个人:
//进程必然是包含了多个线程;线程是依附于进程存在的;如果进程不在了,线程也随之消失了;
//三、句柄:是一个long了类型的数字,对应这计算机程序中的某一个小部件;要操作程序,对应的最小的单位;
//公司中的任何一个物件--编号--计算机---会给计算机坐个编号---(部门---座位号)
//四、为什么计算机可以多线程?跟计算机的CPU的核数有关;
//6核12线程:把六个核心进行逻辑切分:如果有动作需要计算机响应,操作系统就会去申请CPU来处理;CPU在执行动作的时候,CPU是分片执行:
//分片:把每一个核每一毫秒可以执行的动作,切分成10000份;操作系统在调度的时候,执行动作,切分后去执行的多个动作的时候;开始动作A,使用的是分片后的某一份; 结束的时候,可能使用的是另外的某一份;
//计算机在不断的调度过程中;
//1.从宏观角度来说:多个动作,就可以做到在某一段时间内;看似同时执行完毕了;
//2.从微观的角度来说:某一时刻,同意时刻只能处理一件事儿--串行执行;
//五、C#中的多线程:Thread/ThreadPool/Task 都是C#语言在操作计算机的资源(线程)时;封装的一个帮助类库;
//线程:---同步异步:
//单线程执行代码命令:同步执行;
//同步方法:
//1.线性执行,从上往下依次执行--很符合我们人类的思维逻辑,线程ID为01 (主线程/UI线程)
// 请人吃饭:真心的请人吃饭;Richard老师请Vn吃饭;说:Vn,晚上一起去吃饭;Vn:我还要忙一会儿,Richard:那好,我等你;等你忙完,我们一起去吃饭;
//2.同步方法执行慢;卡顿界面--只有一个线程参与计算---CPU太忙,根本无暇他顾;只有一个线程执行动作; 消耗的计算机资源少(工钱少)
//3.同步方法:有序执行;
//多线程执行代码命令:异步执行:
//异步方法:
//1.开启了一个新的线程(多线程--子线程)(有一个新的线程Id);且不再是线型执行了; UI线程不等待子线程执行完毕;主线程直接往后执行; 子线程延迟执行
//请人吃饭:客气一下请人吃饭:Richard老师客气一下请人Vn吃饭,说:Vn,晚上一起吃饭,Vn:我还要忙一会儿,Richard:那你先忙吧,我就先去吃饭了,你忙完自己去吃饭吧!
//2.异步方法执行快:不卡顿界面;开启了一个新的线程去执行去了;开启了五个线程去执行动作:消耗的计算机资源多(工钱多)
// 异步方法--多线程方法--以资源换时间; 使用大量的资源,降低时间成本;
//3.无序执行:线程开启无法控制谁先谁后;线程执行计算结束也无法控制谁先谁后;---多线程异步执行:无序执行;
#endregion
//Thread
//ThreadPool
//Task
/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
for (int i = 0; i < 5; i++)
{
DoSomething("kulala");
}
Debug.WriteLine($"****************btnSync_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsnyc_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
#region 委托异步调用
//在ASP.NET Core的年代:BeginInvoke不再支持了;如果想要看看这个,可以参考第14期;
//Action<string> action = this.DoSomething;
//AsyncCallback asyncCallback = null;
//action.BeginInvoke(" Richard ", asyncCallback, "Object");
#endregion
//Task开启了一个线程
for (int i = 0; i < 5; i++)
{
Task.Run(() =>
{
DoSomething("kulala");
});
}
Debug.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#region Private Method
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomething(string name)
{
Debug.WriteLine($"****************DoSomething Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000_000_000; i++)
{
lResult += i;
}
//Thread.Sleep(2000);
Debug.WriteLine($"****************DoSomething End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
/// <summary>
/// Task:
///
///在最老版本中,有Thread/ThreadPool
///在前几天,在QQ大群有相关的公告的,你们下载观看
///1..NetFramework3.0时代实现
///2.C#中多线程操作的最佳实现---丰富的Api,我们在开发中遇到个各种场景,他都可以满足到;3.Task操作的线程来自于线程/池
///Task,Prallel他们所操作的线程都是来自于线程池;
///
///确定线程的标识:线程Id,线程Id不一样,线程其实就是不同的线程;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTask_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************btnTask_Click Start|| 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//一、Task开启线程有哪些方式:
#region MyRegion
{
//Task task = new Task(new Action(() =>
//{
// Debug.WriteLine("欢迎大家来到.NET高级班进阶学习");
// this.DoSomething("Richard");
//}));
//task.Start();//线程开启了
}
{
// Task.Run(() =>
// {
// this.DoSomething("Richard");
// });
// Task.Run<int>(() =>
// {
// return DateTime.Now.Year;
// });
}
{
// TaskFactory factory = Task.Factory;
// factory.StartNew(() =>
// {
// this.DoSomething("一意");
// });
}
{
//TaskFactory factory = new TaskFactory();
//Task<int> tTask = factory.StartNew<int>(() => DateTime.Now.Year);
}
#endregion
//父子级线程
//一个Task内部,开启了几个线程以后;Task内部的线程可以理解为子线程;
//父线程在等待的时候,子线程没有线程等待;子线程可能内容还没有执行万一,父线程就已经结束了;
{
Task task = new Task(() =>
{
Debug.WriteLine($"****************task开始了: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Task task1 = new Task(() =>
{
Debug.WriteLine($"****************task1: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Thread.Sleep(1000);
Debug.WriteLine("我是task1线程");
});
Task task2 = new Task(() =>
{
Debug.WriteLine($"****************task2: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
Thread.Sleep(1000);
Debug.WriteLine("我是task2线程");
});
task1.Start();
task2.Start();
//task1.Wait();
//task2.Wait();
Debug.WriteLine($"****************task: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
});
task.Start();//线程启动了
task.Wait(); //单个线程的等待;等待task 内部的内容执行完毕,这里会卡顿界面;是主线程等待;
// task.Wait();//等待执行完成:
task.Wait(TimeSpan.FromMilliseconds(1100));
task.Wait(1000);//限时等待;过时不候
// Thread thread = null;
//thread.Abort();//线程停止;
//thread.Start();
//thread.IsBackground = true;//后台线程:进程结束,线程随之消失
//thread.IsBackground = false;//前台线程:进程结束,线程把内部需要执行的动作执行完毕,然后消失
}
//TaskCreationOptions.PreferFairness 相对来说比较公平执行的:如果是先申请的线程,就会优先执行;
{
//Task task1= new Task(() =>
//{
//}, TaskCreationOptions.PreferFairness);
//Task task2 = new Task(() =>
//{
//}, TaskCreationOptions.PreferFairness);
}
{
// {
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// Thread.Sleep(3000);//主线程执行到这儿会卡顿等待;等待3000ms后继续往后;
// stopwatch.Stop();
// Debug.WriteLine($"Thread.Sleep(3000):{stopwatch.ElapsedMilliseconds}");
// }
// {
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// //回调:Delay一般和ContinueWith配合使用;
// //不卡顿界面:3000ms以后,去执行一段业务逻辑:执行的动作就是ContinueWith内部的委托;ContinueWith内部的执行有可能是一个全新的线程去执行,也有可能是主线程去执行;
// Task.Delay(3000).ContinueWith(t =>
// {
// Debug.WriteLine($"****************Delay || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
// stopwatch.Stop();
// Debug.WriteLine($"Task.Delay(3000):{stopwatch.ElapsedMilliseconds}");
// });//一般情况下,这个Delay 是等待3000ms继续做点什么---或者说延迟3000ms后做点什么;
// }
}
//现在是20:56,大家休息5分钟&&上厕所,21:01继续~~
//
//多线程:他的应用场景:
//一、在什么情况下,可以使用多线程?
//1.一个数据库查询:---可以使用多线程吗?不适合使用多线程;因为只有一个动作;不存在多个动作并发执行;
//2.如果要同时去查询接口+查询缓存+查询数据库?--适合使用多线程吗?---可以开启三个线程;每个线程就负责一个动作;
//通过一个案例,来解析:
//二、高级班VIP课程讲课:
//1.开始上课---Richard老师开始讲课--一节一节的开始讲:适合使用多线程吗?--不适合:不能同时讲多个课
//。。。。。经过大概8个月的时间
//2.开始项目实战---分组进行;多人合作完成; 适合使用多线程吗? ---适合;每个人就可以对应一个线程;多个人同时去开发功能---多线程并发;
//3.如果大家其中有一个人完成了功能的开发,Richard老师就要开始准备环境了。
//a.需要等待一堆线程中的某一个线程执行结束,马上触发一个动作;
//解决方案: Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待---会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
//4.如果所有人开发完毕了,庆祝一下,一起吃个饭;
//三、应用场景:
//1.ContinueWhenAny
//如果需要在一堆任务中,某一种执行结束后,去触发一个动作;
//2.ContinueWhenAll
//如果需要回调---如果需要控制顺序:
//3.WaitAny
// 有一个列表数据:有可能是来自于第三方接口,有可能是来自于缓存,也有可能是来自于数据库,查询的时候,我们不确定;
// 常规玩法: 先找缓存,如果有数据,直接返回了,如果缓存没有--可能要去查询数据库,如果数据库有数据,直接返回,如果没有---查询第三方接口;
// 多线程:同时开启三个线程,同时去查询三个地方的数据,只要是有一个地方的数据查询到了,只要是有一个地方的数据找到了,其他的两个线程我们就不管了;直接把找到的数据返回给前端就可以了;
//
//
//4.WaitAll
// 如果有一个首页,首页中包含了很多功能:考勤信息,年度top10,季度top10,公告,通知;
// 在主页中:要加载主页-这些信息都需要查询出来: 而且这些信息都是来自于不同的地方,有的来自于接口,有的来自于缓存有的都来于数据库:
//有几个块信息,就开启几个线程,同时分别去各自的地方进行查询各自的数据;可以在后台等待所有的子线程执行结束;拿到结果、组件一个大的复杂实体对象,传递给视图,通过视图做绑定;--让查询并发执行: 但是这里如果是使用C#开发,不做前后端分离的话,可以考虑这种,可以提高性能;
//现在更多的时候,可能是使用异步Ajax来完成---用户体验会更好; 资源会有所浪费,但是性能可以提高;
{
Debug.WriteLine("第15期高级班课程起飞。。。");
Debug.WriteLine("Richard老师开始讲课了。。。。");
this.Teach("跨平台开发.NET5");
this.Teach("高并发组件Redis/Nginx/RabbitMQ/Memcache/Mongo...");
this.Teach("各种框架扩展定制。。。。");
Debug.WriteLine("Richard老师知识点已经讲完了。。。。。");
Debug.WriteLine("开始项目实战----直播平台实战课");
List<Task> taskList = new List<Task>();
TaskFactory factory = new TaskFactory();
taskList.Add(factory.StartNew(obj => Coding("嘟嘟", "权限的数据库设计"), "嘟嘟"));
taskList.Add(factory.StartNew(obj => Coding("年轻人不讲武德", "WeChat接口对接"), "年轻人不讲武德"));
taskList.Add(factory.StartNew(obj => Coding("闻", "脚手架框架搭建"), "闻"));
taskList.Add(factory.StartNew(obj => Coding("超越", "ABPVNetxt框架搭建"), "超越"));
taskList.Add(factory.StartNew(obj => Coding("角印", "编写Webapi"), "角印"));
{
//相当于是一个回调;主线程执行的时候,这里是不卡段界面的;当taskList集合中的某一个线程执行结束语了;就触发后面委托中的动作;---不卡顿界面--用户体验就更好
factory.ContinueWhenAny(taskList.ToArray(), ts =>
{
Debug.WriteLine($"{ts.AsyncState}同学开发完毕,Richard老师开始准备环境。。。");
});
//
}
{
factory.ContinueWhenAll(taskList.ToArray(), ts =>
{
Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
});
}
{
//Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待-- - 会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
Debug.WriteLine("xxxx同学开发完毕,Richard老师开始准备环境。。。");
}
{
//Task.WaitAll--等待几个线程都执行结束;---主线程会等待-- 一直到所有的线程执行结束,才往后执行;- 会卡顿界面;--体验不好
Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
Task.WaitAll(taskList.ToArray());
Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
}
List<Action> actions = new List<Action>();
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
actions.Add(() => { });
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 2; //下面在开启线程执行委托的时候,最多开启2个线程;
Parallel.Invoke(options, actions.ToArray()); //会开启四个线程分别去执行;
//如果想要限制线程数量
}
Debug.WriteLine($"****************btnTask_Click End || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#region Private Method
/// <summary>
/// 模拟了讲课的过程:
/// </summary>
/// <param name="lesson"></param>
private void Teach(string lesson)
{
Debug.WriteLine($"{lesson} ||开始了。。。");
long lResult = 0;
//for (int i = 0; i < 1_000_000_000; i++)
//{
// lResult += i;
//}
Debug.WriteLine($"{lesson} || 讲完了。。。");
}
/// <summary>
/// 模拟Coding过程
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name, string projectName)
{
Debug.WriteLine($"****************Coding Start || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1_000_000_000; i++)
{
lResult += i;
}
Thread.Sleep(3000);
Debug.WriteLine($"****************Coding End || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
/// <summary>
/// Parallel
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnParallel_Click(object sender, EventArgs e)
{
//如果我需要一次开启多个线程来执行一部分动作呢?
Debug.WriteLine($"****************btnParallel_Click Start || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
{
{
//List<Action> acitonList = new List<Action>();
//acitonList.Add(() => { });
//acitonList.Add(() => { });
//acitonList.Add(() => { });
//Parallel.Invoke(acitonList.ToArray());//也是可以开启线程,可以一次放入很多个委托去执行;
}
//一、Parallel
//1.可以传入多个委托
//2.多个委托中的内容是会开启线程来执行---执行这里的线程有可能是新的线程,也有可能是主线程参与计算的
//3.会阻塞主线程---相当于是主线程等待子线程执行结束
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3;
Parallel.Invoke(options,
() =>
{
Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.DoSomething("this is Action 01");
},
() =>
{
Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.DoSomething("this is Action 02");
},
() =>
{
Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.DoSomething("this is Action 03");
});
}
//二、能不能然他不阻塞界面?包一个Task
{
//Task.Run(() =>
//{
// Parallel.Invoke(
// () =>
// {
// Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
// this.DoSomething("this is Action 01");
// },
// () =>
// {
// Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
// this.DoSomething("this is Action 02");
// },
// () =>
// {
// Debug.WriteLine($"线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
// this.DoSomething("this is Action 03");
// });
//});
}
//三、还不是开启了多个线程去执行委托?有啥作用:--Parallel是基于Task一个封装;
//1.既然是开启多个线程--Parallel.For
//2.如果有一大堆的任务要执行;100个任务---需要多线来执行:100个任务---开启100个线程?--不好
//2.控制线程的数量---Parallel就是可以控制线程的数量,不至于出现线程泛滥的情况
//
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3;
Parallel.For(0, 100, options, index =>
{
Debug.WriteLine($"index:{ index} 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.DoSomething("this is Action 03");
});
}
{
List<int> intlist = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; //100个
//100个任务: 如果我使用Parallel来开启线程计算;线程会有100个线程来执行--线程数量会过多;
//1.最好能够控制线程数量 100个任务; 限定开启5个线程;控制反正一共就只有五个线程参与计算
//2.相当于5个线程去平摊这100个任务---既开启了多个线程来执行任务---提高性能---且没有过多的开启线程(过多的开启线程会过多的损耗计算机的资源);
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3; //会有17个任务,限定了3个线程去执行这17个任务
Parallel.ForEach(intlist, options, s =>
{
Debug.WriteLine($"index:{ s} 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
this.DoSomething("this is Action 03");
});
}
}
Debug.WriteLine($"****************btnParallel_Click End || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// task进阶
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TsakAdvanced_Click(object sender, EventArgs e)
{
Debug.WriteLine($"****************TsakAdvanced_Click start || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//一、多线程中的异常情况
//1.你们在开发中,如果要捕捉异常---普通方法--try-catch
//2.多线程中,如果发生异常,使用try-catch包裹,捕捉不到异常
//3.异常去哪儿了呢? 异常肯定是被吞掉了
//4.多线程中,如果要捕捉异常---怎么做?
//5.多线程中,如果要捕捉异常,需要设置主线程等待子线程执行结束;可以捕捉到异常
//6.多线程内部发生异常后,抛出的异常类型是:system.AggregateException(多线程的异常类型)
//7.如果(try-catch--catch)给定多个异常类型类匹配,多线程中,异常之后,有限配具体,如果没有具体,再匹配抽象
{
try
{
{
//Task task = Task.Run(() =>
// {
// int i = 0;
// int j = 10;
// int k = j / i; //尝试除以0 会异常
// });
//List<Task> tasks = new List<Task>();
//tasks.Add(task);
//Task.WaitAll(tasks.ToArray());
}
List<Task> tasklist = new List<Task>();
for (int i = 0; i < 20; i++)
{
string keywork = $"TsakAdvanced_Click_{i}";
tasklist.Add(Task.Run(() =>
{
Thread.Sleep(new Random().Next(50, 100));
if (keywork == "TsakAdvanced_Click_6")
{
throw new Exception("TsakAdvanced_Click_6");
}
else if (keywork == "TsakAdvanced_Click_9")
{
throw new Exception("TsakAdvanced_Click_9");
}
else if (keywork == "TsakAdvanced_Click_12")
{
throw new Exception("TsakAdvanced_Click_12");
}
}));
}
Task.WaitAll(tasklist.ToArray());
}
catch (AggregateException aex) //具体
{
Debug.WriteLine(aex.Message);
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
catch (Exception ex) //父类(一切异常的父类) 抽象
{
Debug.WriteLine(ex.Message);
}
}
//故事、如果三个线程去执行一个业务逻辑体(查询---线程1--执行修改数据库;线程2---执行修改缓存,线程三--执行一个调用接口去修改另外一个服务器上的数据)
//以上这个逻辑体执行过程中,如果有一个线程是异常了;在这个逻辑体中,其实就是一个残缺的--表示整个逻辑就有问题!
//多线程执行的时候,有时候,必须是多个线程都执行成功,才算成功,只要有一个线程异常了,就表示都异常----既然都是异常了,就应该让线程取消;已经异常了,就没哟必须继续往后了
//二、线程取消; 线程是无法从外部取消的(除非关闭进程),只能自己取消自己---只有上帝才能打败上帝;
//1.线程如何自己取消自己呢?----线程自己取消---其实就是向外抛出一个异常了;
//关于Thread/ThreadPool 看过录播的刷个1 否则刷个2
//2.如何取消线程呢?---具体场景中,应该怎么做呢?
//3.标准的线程取消
// a) 实例化一个 CancellationTokenSource
// b) 包含了一个IsCancellationRequested 属性,属性值默认为false
// c) 包含了一个Cancel方法--Cancel 方法如果被执行--CancellationTokenSource 内部的属性值马上---false--true; 且只能从 false--true 不能由true --false
//cts线程取消--看哪个线程跑的慢,如果有一个线程发生了异常了,跑的慢的线程,就会被取消掉(异常掉),如果跑的都比较快的线程--当还没有线程发生异常的时候,这个比较快的线程已经结束了,那么这个结束的线程是无法被取消的;
//4. 当有一个线程发生异常的时候,其他线程就会有三种情况 1.已经结束的线程--不管(管不住); 2.已经正常开始,我可以让你取消,让这线程在结束的时候,抛出异常(取消了);3.还有部分的线程根本都还没有开启:只要有异常的线程,就应该让没有开启的线程不再开启了
//
{
//Thread thread = null;
//thread.Abort(); //取消线程
}
{
//Task task = Task.Run(() =>
// {
// int i = 0;
// int j = 10;
// int k = j / i; //尝试除以0 会异常
// });
//List<Task> tasks = new List<Task>();
//tasks.Add(task);
//Task.WaitAll(tasks.ToArray());
}
try
{
List<Task> tasks = new List<Task>();
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < 100; i++)
{
string keywork = $"TsakAdvanced_Click_{i}";
tasks.Add(Task.Run(() =>
{
Thread.Sleep(new Random().Next(10, 300));
//if (cts.IsCancellationRequested == false)
//{
// Debug.WriteLine($" 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 正常开始了");
//}
//else
//{
// Debug.WriteLine($" 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 没有正常开始了");
// //throw new Exception($"{Thread.CurrentThread.ManagedThreadId.ToString("00") }取消了。。。");
//}
cts.Token.ThrowIfCancellationRequested();
try
{
if (keywork == "TsakAdvanced_Click_6")
{
throw new Exception("TsakAdvanced_Click_6");
}
}
catch (Exception)
{
cts.Cancel();
}
cts.Token.ThrowIfCancellationRequested();
//if (cts.IsCancellationRequested == false)
//{
// Debug.WriteLine($" 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 正常结束了。。。");
//}
//else
//{
// Debug.WriteLine($" 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 没有正常结束了。。。");
// //throw new Exception($"{Thread.CurrentThread.ManagedThreadId.ToString("00") }取消了。。。" );
//}
}, cts.Token)); //有线程发生异常后,还没有开启的线程就不再开启了
}
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException aex)
{
foreach (var exception in aex.InnerExceptions)
{
Debug.WriteLine(exception.Message);
}
}
Debug.WriteLine($"****************TsakAdvanced_Click End || 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
}
}