概述
/// 进程 线程 多线程 计算机概念
/// 进程:一个程序运行时,占用的全部计算资源的总和
/// 线程:程序执行流的最小单位;任何操作都是由线程完成的;
/// 线程是依托于进程存在的,一个进程可以包含多个线程;
/// 线程也可以有自己的计算资源
/// 多线程:多个执行流同时运行
/// 1 CPU太快了,分时间片--上下文切换(加载环境--计算--保存环境)
/// 微观角度,一个核同一时刻只能执行一个线程;宏观的来说是多线程并发
/// 2 多CPU多核 可以独立工作
/// 4核8线程--核是物理的核 线程是指虚拟核
///
/// Thread是C#语言对线程对象的封装
///
/// 是对方法执行的描述
/// 同步:完成计算之后,再进入下一行
/// 异步:不会等待方法的完成,会直接进入下一行 非阻塞
///
/// C# 异步和多线程有什么差别
/// 多线程就是多个thread并发;
/// 异步是硬件式的异步
/// 异步多线程--thread pool task
/// 异步方法
/// 1 同步方法卡界面,主(UI)线程忙于计算;
/// 异步多线程方法不卡界面,主线程完事儿了,计算任务交给子线程在做;
/// winform提升用户体验;web一个业务操作后要发邮件,异步发送邮件
///
/// 2 同步方法慢,只有一个线程干活;
/// 异步多线程方法快,因为多个线程并发运算;
/// 并不是线性增长,a资源换时间,可能资源不够 b 多线程也有管理成本
/// 但并不是越多越好
/// 多个独立任务可以同时运行;
///
/// 3 异步多线程无序:启动无序 执行时间不确定 结束也无序
/// 一定不要通过等几毫秒的形式来控制启动/执行时间/结束
///
/// 回调/状态等待/信号量
回调
callback回调函数会在action执行完执行,第三个参数"hao"可以通过Console.WriteLine(ia.AsyncState);显示出来。
Action<string> action = this.DoSomethingLong;
//IAsyncResult asyncResult = null;
//AsyncCallback callback = ia =>
//{
// //Console.WriteLine(object.ReferenceEquals(asyncResult, ia));
// //Console.WriteLine(ia.AsyncState);
// //Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
//};
//asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao");
c#的多线程发展中主要有3次大变动,分别thread,threadpool,task
目前task是主流,前两种就不记录了
Task简介
什么时候用多线程? 任务能并发运行;提升速度;优化体验
例:
////一个业务查询操作有多个数据源 首页–多线程并发–拿到全部数据后才能返回 WaitAll
////一个商品搜素操作有多个数据源,商品搜索–多个数据源–多线程并发–只需要一个结果即可–WaitAny
任务的并行:parallel
多线程中的异常处理
在try里面最后加一个Task.WaitAll(taskList.ToArray());才能捕捉到异常,类型为AggregateException,
否则线程里面的异常是会被吞掉了,因为已经脱离try catch的范围了
线程取消
多个线程并发,某个失败后,希望通知别的线程,都停下来
task是外部无法中止,Thread.Abort不靠谱,因为线程是OS的资源,无法掌控啥时候取消
线程自己停止自己–公共的访问变量–修改它—线程不断的检测它(延迟少不了)
////CancellationTokenSource去标志任务是否取消 Cancel取消 IsCancellationRequested 是否已经取消了
////Token 启动Task的时候传入,那么如果Cancel了,这个任务会放弃启动,抛出一个异常
//CancellationTokenSource cts = new CancellationTokenSource();//bool值 //bool flag = true;
//for (int i = 0; i < 40; i++)
//{
// string name = string.Format("btnThreadCore_Click{0}", i);
// Action<object> act = t =>
// {
// try
// {
// //if (cts.IsCancellationRequested)
// //{
// // Console.WriteLine("{0} 取消一个任务的执行", t);
// //}
// Thread.Sleep(2000);
// if (t.ToString().Equals("btnThreadCore_Click11"))
// {
// throw new Exception(string.Format("{0} 执行失败", t));
// }
// if (t.ToString().Equals("btnThreadCore_Click12"))
// {
// throw new Exception(string.Format("{0} 执行失败", t));
// }
// if (cts.IsCancellationRequested)//检查信号量
// {
// Console.WriteLine("{0} 放弃执行", t);
// return;
// }
// else
// {
// Console.WriteLine("{0} 执行成功", t);
// }
// }
// catch (Exception ex)
// {
// cts.Cancel();
// Console.WriteLine(ex.Message);
// }
// };
// taskList.Add(taskFactory.StartNew(act, name, cts.Token));
//}
//Task.WaitAll(taskList.ToArray());
多线程临时变量
//i最后是5 全程就只有一个i 等着打印的时候,i==5
//k 全程有5个k 分别是0 1 2 3 4
//k在外面声明 全程就只有一个k 等着打印的时候,k==4
//int k = 0;
//for (int i = 0; i < 5; i++)
//{
// k = i;
// new Action(() =>
// {
// Thread.Sleep(100);
// Console.WriteLine($"k={k} i={i}");
// }).BeginInvoke(null, null);
//}
异步执行并且线程停止了100,最后运行读取i时有已经自增为5
线程安全
//lock 解决,因为只有一个线程可以进去,没有并发,所以解决了问题 但是牺牲了性能,所以要尽量缩小lock的范围
//不要冲突–数据拆分,避免冲突
//安全队列 ConcurrentQueue 一个线程去完成操作
https://blog.csdn.net/jiangxinyu/article/details/5380969