基本概念:
什么是进程,线程和多线程:
进程:一个程序运行时,占用的全部计算资源的总和;进程之间是相互独立的。
线程:
- 是windows任务调度的最小单位。
- 线程是程序中的一个执行流
- 所有的代码都必须执行在线程,有线程才能有代码执行
多线程:多个执行流同时运行
什么是同步异步:
他们只是对方法执行的描述
同步:完成一行计算后,再进入下一行。
异步:不会等待方法的完成,直接进入下一步操作。
异步多线程:
优点:1. 不卡界面,提升用户体验
2.同步方法只有一个线程干活,速度慢;异步方法有多个线程一起干活速度快
3.异步多线程,子线程启动无序,结束也无序。
训练准备:
一:首先创建一个控制台应用程序,添加Window窗体,定义几个按钮,开始进行Demo:
二:控制台Program.cs写法
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
三:再Form1.cs定义一个方法方便后续使用
private void DoSomeThing(string name)
{
Console.WriteLine($"*************DoSomeThing 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{name}{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
Console.WriteLine($"*************DoSomeThing 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{name}{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
Thread:
出现在1.0 1.1版本,基本摒弃不怎么使用,不需要解释,常用方法:
private void Threads_Click(object sender, EventArgs e)
{
Console.WriteLine($"=====================Thread 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
ThreadStart threadStart = () => this.DoSomeThing("Threads_Click");
Thread thread = new Thread(threadStart);
thread.Start();//开始线程
//thread.Suspend();//线程挂起 已弃用
//thread.Resume();//线程唤醒 已弃用
//try
//{
// thread.Abort();//销毁线程,方式为抛异常 不建议使用 不一定即时/有些动作发出可能停不下来
//}
//catch (Exception)
//{
// Thread.ResetAbort();//取消异常
// throw;
//}
//线程等待
//thread.Join(500);//最多等待500毫秒
//Console.WriteLine("最多等待500毫秒");
//thread.Join();//当前线程等待Thread完成
//while (thread.ThreadState!=ThreadState.Stopped)
//{
// Thread.Sleep(100);//当前线程休息100毫秒
//}
//thread.IsBackground = true;//指定后台线程:随着进程退出,默认为False,为前台线程,前台线程启动之后一定要完成任务
//thread.Priority = ThreadPriority.Highest;//线程优先级 CPU会优先执行 Highest 不代表说Highest就最先完成
Console.WriteLine($"=====================Thread 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
}
ThreadPool----线程池:
出现在2.0版本,优点:避免无线使用线程,加以限制。而且能够重用线程,避免重复创建和销毁
private void btnThreadPool_Click(object sender, EventArgs e)
{
Console.WriteLine($"=====================ThreadPool 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
//ThreadPool.QueueUserWorkItem(t => this.DoSomeThing("btnThreadPool_Click"));//--------------启动多线程
//获取线程池请求最大的数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
{
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"workerThreads=【{workerThreads}】----completionPortThreads=【{completionPortThreads}】");
}
//获取线程池请求最大的数目的最小数量。
{
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"workerThreads=【{workerThreads}】----completionPortThreads=【{completionPortThreads}】");
}
//设置线程池最大的线程数
ThreadPool.SetMaxThreads(16, 16);
//设置线程池最小的线程数
ThreadPool.SetMinThreads(8, 8);
Console.WriteLine("-----------------------------------设置后-----------------------------------------");
//获取线程池请求最大的数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
{
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"workerThreads=【{workerThreads}】----completionPortThreads=【{completionPortThreads}】");
}
//获取线程池请求最大的数目的最小数量。
{
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"workerThreads=【{workerThreads}】----completionPortThreads=【{completionPortThreads}】");
}
//-----------------------------------------线程等待---------------------------------------
//ManualResetEvent为一个类,包含了一个布尔属性
//false时,WaitOne等待,当执行Set时,ManualResetEvent变为true,不进行等待直接过去
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(t =>
{
this.DoSomeThing("btnThreadPool_Click");
manualResetEvent.Set();//ManualResetEvent变为true
//manualResetEvent.Reset();//相反,设置为false
});
//一般来说,不要阻塞线程池的线程
manualResetEvent.WaitOne();
Console.WriteLine("等待QueueUserWorkItem完成后才执行");
Console.WriteLine($"=====================ThreadPool 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
}
Task----最常用的
3.0版本出现,基于ThreadPool(线程池)
/// <summary>
/// 编码做项目
/// </summary>
/// <param name="name"></param>
/// <param name="project"></param>
private void Coding(string name, string project)
{
Console.WriteLine($"****************Coding {name} Start {project} 【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
//Thread.Sleep(2000);
Console.WriteLine($"****************Coding {name} End {project} 【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
private void btnTask_Click(object sender, EventArgs e)
{
Console.WriteLine($"=====================Task 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
//启动多线程的三种方式
//Task.Run(() => this.DoSomeThing("btnTask_Click1"));
//new Task(() => this.DoSomeThing("btnTask_Click2")).Start();
//TaskFactory taskFactory = Task.Factory;//4.0的时候出现
//taskFactory.StartNew(() => this.DoSomeThing("btnTask_Click3"));//同样可以启动多线程
//小场景
//List<Task> taskList = new List<Task>();
//Console.WriteLine($"----------项目经理启动项目--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//Console.WriteLine($"----------前置准备工作--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//Console.WriteLine($"----------开始编程--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//taskList.Add(Task.Run(() => this.Coding("张三", "前端")));
//taskList.Add(Task.Run(() => this.Coding("李四", "后台")));
//taskList.Add(Task.Run(() => this.Coding("王五", "服务")));
//taskList.Add(Task.Run(() => this.Coding("赵六", "测试")));
//taskList.Add(Task.Run(() => this.Coding("田七", "运维")));
//Task.WaitAny(taskList.ToArray());//会阻塞当前线程,等某个任务完成后,才进入下一行,会卡界面
//Task.WaitAny(taskList.ToArray(),1000);//限时等待,会卡界面
//Console.WriteLine($"--------------------完成一部分,简单庆祝----------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//Task.WaitAll(taskList.ToArray());//会阻塞当前线程,等全部任务完成后,才进入下一行,会卡界面
//Task.WaitAll(taskList.ToArray(), 1000);//限时等待,会卡界面
//Console.WriteLine("等待了一秒");
//子线程干的任务,不卡界面
//{
// Task.WhenAny(taskList.ToArray()).ContinueWith(t =>
// {
// Console.WriteLine($"------------------第一个完成--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
// Task.WhenAll(taskList.ToArray()).ContinueWith(t =>
// {
// Console.WriteLine($"------------------部署环境,联调测试--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
//}
//和上面一样的效果
//{
// TaskFactory taskFactory = Task.Factory;
// taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
// {
// Console.WriteLine($"------------------第一个完成--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
// taskFactory.ContinueWhenAll(taskList.ToArray(), t =>
// {
// Console.WriteLine($"------------------部署环境,联调测试--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
//}
//{
// List<int> list = new List<int>();
// for (int i = 0; i < 10000; i++)
// {
// list.Add(i);
// }
// //完成10000个任务,但只要11个线程
// Action<int> action = i =>
// {
// Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00"));
// Thread.Sleep(new Random(i).Next(100, 300));
// };
// List<Task> taskList = new List<Task>();
// foreach (var i in list)
// {
// int k = i;
// taskList.Add(Task.Run(() => action.Invoke(k)));
// if (taskList.Count>10)
// {
// Task.WaitAny(taskList.ToArray());
// taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
// }
// }
// Task.WhenAll(taskList.ToArray());
//}
//{
// TaskFactory taskFactory = new TaskFactory();
// List<Task> tasksList = new List<Task>();
// tasksList.Add(taskFactory.StartNew(t => this.Coding("张三", "开发"), "张三"));
// tasksList.Add(taskFactory.StartNew(t => this.Coding("李四", "运维"), "李四"));
// tasksList.Add(taskFactory.StartNew(t => this.Coding("王五", "测试"), "王五"));
// taskFactory.ContinueWhenAny(tasksList.ToArray(), t =>
// {
// Console.WriteLine(t.AsyncState);
// Console.WriteLine($"------------------第一个完成--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
// taskFactory.ContinueWhenAll(tasksList.ToArray(), t =>
// {
// Console.WriteLine(t[t.Count()-1].AsyncState);
// Console.WriteLine($"------------------部署环境,联调测试--------------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
//}
//没有什么是包一层解决不了的,实现WaitAny和WaitAll不卡界面
//{
// List<Task> taskList = new List<Task>();
// Console.WriteLine($"----------项目经理启动项目--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// Console.WriteLine($"----------前置准备工作--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// Console.WriteLine($"----------开始编程--------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// Task.Run(() =>
// {
// taskList.Add(Task.Run(() => this.Coding("张三", "前端")));
// taskList.Add(Task.Run(() => this.Coding("李四", "后台")));
// taskList.Add(Task.Run(() => this.Coding("王五", "服务")));
// taskList.Add(Task.Run(() => this.Coding("赵六", "测试")));
// taskList.Add(Task.Run(() => this.Coding("田七", "运维")));
// Task.WaitAny(taskList.ToArray());//会阻塞当前线程,等某个任务完成后,才进入下一行,会卡界面
// Task.WaitAny(taskList.ToArray(), 1000);//限时等待,会卡界面
// Console.WriteLine($"--------------------完成一部分,简单庆祝----------------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// Task.WaitAll(taskList.ToArray());//会阻塞当前线程,等全部任务完成后,才进入下一行,会卡界面
// Task.WaitAll(taskList.ToArray(), 1000);//限时等待,会卡界面
// Console.WriteLine("等待了一秒");
// //全部任务都完成才进行的操作
// Console.WriteLine($"----------甲方验收,上线使用---------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
// });
//}
Task.Delay(1000);//延迟 不会卡
Thread.Sleep(1000);//等待
//全部任务都完成才进行的操作
//Console.WriteLine($"----------甲方验收,上线使用---------【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"=====================Task 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
}
Parallel
并行编程,在Task的基础上做了封装
卡界面,主线程参与运算,节约了一个线程
private void btnParallel_Click(object sender, EventArgs e)
{
Console.WriteLine($"=====================Parallel 开始【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
//{
// Parallel.Invoke(
// () => Coding("张三", "前端")
// , () => Coding("李四", "后端")
// , () => Coding("王五", "运维")
// , () => Coding("赵六", "测试")
// );
//}
//{
// Parallel.For(0, 5, i => this.Coding("张三", "开发"));
//}
//{
// Parallel.ForEach(new string[] {"1","2","3" }, i => this.Coding("张三", "开发"));
//}
{
//最多三个线程计算,控制并发数量
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 3;
Parallel.For(0, 5, parallelOptions, i => this.Coding("张三", "开发"));
}
Console.WriteLine($"=====================Parallel 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
}
多线程异常处理,线程取消,临时变量,锁
private static readonly object btnThreadCore_Click_Lock = new object();
private int TotalCount = 0;//
private List<int> IntList = new List<int>();
private void btnThreadCode_Click(object sender, EventArgs e)
{
Console.WriteLine($"=====================ThreadCode 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
try
{
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
#region 异常处理
//线程里面的异常是被吞掉了,因为已经脱离try catch的范围了 WaitAll 抓到多线程里面全部的异常
//线程里面的action不允许出现异常,自己处理好
//for (int i = 0; i < 20; i++)
//{
// string name = string.Format($"btnThreadCore_Click_{i}");
// Action<object> act = t =>
// {
// try
// {
// Thread.Sleep(2000);
// if (t.ToString().Equals("btnThreadCore_Click_11"))
// {
// throw new Exception(string.Format($"{t} 执行失败"));
// }
// if (t.ToString().Equals("btnThreadCore_Click_12"))
// {
// throw new Exception(string.Format($"{t} 执行失败"));
// }
// Console.WriteLine("{0} 执行成功", t);
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Exception:{ex.Message}");
// }
// };
// taskList.Add(taskFactory.StartNew(act, name));
//}
//Task.WaitAll(taskList.ToArray());
#endregion
#region 线程取消
//多个线程并发,某个失败后,希望通知别的线程,都停下来
//task是外部无法中止,Thread.Abort不靠谱,因为线程是OS的资源,无法掌控啥时候取消
//线程自己停止自己--公共的访问变量--修改它---线程不断的检测它(延迟少不了)
//CancellationTokenSource去标志任务是否取消 Cancel取消 IsCancellationRequested 是否已经取消了
//Token 启动Task的时候传入,那么如果Cancel了,这个任务会放弃启动,抛出一个异常
CancellationTokenSource cts = new CancellationTokenSource();
//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());
#endregion
#region 多线程临时变量
//for (int i = 0; i < 5; i++)
//{
// Task.Run(() =>
// {
// Thread.Sleep(100);
// Console.WriteLine(i);
// });
//}
//for (int i = 0; i < 5; i++)
//{
// int k = i;
// Task.Run(() =>
// {
// Thread.Sleep(100);
// Console.WriteLine(k);
// });
//}
//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);
//}
#endregion
#region 线程安全 lock
//共有变量:都能访问局部变量/全局变量/数据库的一个值/硬盘文件
//线程内部不共享的是安全
//lock 解决,因为只有一个线程可以进去,没有并发,所以解决了问题 但是牺牲了性能,所以要尽量缩小lock的范围
//不要冲突--数据拆分,避免冲突
//安全队列 ConcurrentQueue 一个线程去完成操作
int TotalCountIn = 0;//有问题
for (int i = 0; i < 10000; i++)//i有问题
{
int newI = i;//没问题 全新的newI
taskList.Add(taskFactory.StartNew(() =>
{
//int m = 3 + 2;
//string teacher = "Eleven";
//string teacherVip = "Eleven";
//lock (teacher)
//{
//}
//lock (teacherVip)
//{
//}
//lock (m) { }//值类型不能lock
lock (btnThreadCore_Click_Lock)//lock后的方法块,任意时刻只有一个线程可以进入
//只能锁引用类型,占用这个引用链接 不要用string 因为享元
{ //这里就是单线程
this.TotalCount += 1;
TotalCountIn += 1;
this.IntList.Add(newI);
}
//private 防止外面也去lock static 全场唯一 readonly不要改动 object表示引用
//private static readonly object btnThreadCore_Click_Lock = new object();
//lock (this)
//{
// //this form1的实例 每次实例化是不同的锁,同一个实例是相同的锁
// //private readonly object btnThreadCore_Click_Lock = new object();
// //但是这个实例别人也能访问到,别人也能锁定
//}
//Monitor.Enter(btnThreadCore_Click_Lock);
//Monitor.Exit(btnThreadCore_Click_Lock);
}));
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine(this.TotalCount);
Console.WriteLine(TotalCountIn);
Console.WriteLine(this.IntList.Count());
#endregion
}
//通过遍历去查询出错的信息
catch (AggregateException aex)
{
foreach (var item in aex.InnerExceptions)
{
Console.WriteLine(item.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine($"=====================ThreadCode 结束【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}=====================");
}