table of Contents
Preface
由于这一部分内容,之前工作用到过,算是比较熟,但是总结嘛。放我的有点精简了,还是把老师的晾上来吧!
1.Thread
///进程:计算机概念,程序在运行的时候,记录当前程序对计算机的各种资源的消耗的一种记录;虚拟出来;
///线程:也是计算机概念;线程是计算机在执行某一个动作的时候,一个最小的执行流,虚拟出来的一个代名词;
///包含关系:一个进程包含多个线程;
///句柄:句柄其实就是一个数字--对标与计算机程序中的一个最小单位;Id,long类型的数字;
//C#中的多线程:Thread是对计算机资源操作一种封装类;
//为什么可以多线程?
//1.多个CPU可有有多个核可以并行计算;
//8核16线程,线程其实是模拟出来;
//计算机的计算能力超强的;一个线程去操作某一个计算;
//2.CPU分片 1s 能处理1000000次计算;把1s内的处理能力再进一步切分; 操作系统去调用执行不同的计算;
// 从宏观来讲: 就相当于有很多个任务可以去并发执行;
// 从微观来讲:一个物理CPU同一时刻,只能为一个任务服务;
//同步异步:
//同步方法:发起调用,代码执行一行一行的来,按照顺序执行;非常符合我们的开发思维;------有顺序(线程ID是同一个--同一个线程来执行的所有操作);
//请人吃饭:诚心诚意的请人吃饭,邀请 “我要上台” 吃饭,“我要上台”:我现在有点忙,我等你,你忙完以后,我俩一起去吃饭;
//异步方法:发起调用:没有等待完成,直接进入到下一行,启动一个新的线程来执行动作(线程ID不一样);
// 请人吃饭:只是客气一下的请人吃饭,邀请 “我要上台” 吃饭,“我要上台”:我现在有点忙,那先先忙吧,我就先去吃饭了,你忙完自己去吃呗;
//异步方法三大特点:
//1.不卡顿界面:UI线程(主线程)闲置,计算任务都交给子线程去执行了;
//同步方法:卡界面,因为主线程忙于计算,根本无暇他顾;
//改善了用户体验了;场景:发送短信、发送邮件;
//2.同步方法慢,异步方法快;因为多线程并发:以资源换性能;
//同步方法:10s
//异步方法:2.3s
//中间相差了大概五倍;达不到五倍;因为线程的调度也需要消耗资源;
//到不到5倍:CPU密集型计算(资源受限)
//同步方法:10.01s
//异步方法:2.0001s 下面这组更加接近5倍
//Thread.Sleep 不消耗资源,只是等待;
//3.同步方法按顺序执行,异步方法无序性;
//启动无顺序:线程资源是向操作系统申请的,有操作系统的调度策略来决定;所以启动顺序随机的;
//执行也是无顺序;
//通过以上两点:结束当然也是无顺序的;
//难道就不能控制顺序吗?
//其实也可以;
/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
//Thread.CurrentThread.ManagedThreadId 对标于每一个线程的一个ID;
Console.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
int l = 3;
int m = 4;
int n = l + m;
for (int i = 0; i < 5; i++)
{
string name = string.Format($"btnSync_Click_{i}");
this.DoSomethingLong(name);
}
Console.WriteLine($"****************btnSync_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 DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong 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); //线程是闲置的;
Console.WriteLine($"****************DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsync_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
Action<string> action = this.DoSomethingLong;
action.Invoke("btnAsync_Click");
action("btnAsync_Click");
action.BeginInvoke("btnAsync_Click", null, null); //会分配一个新的线程去执行;20200803Core还不支持;
for (int i = 0; i < 5; i++)
{
string name = string.Format($"btnAsync_Click_{i}");
action.BeginInvoke(name, null, null);
}
Console.WriteLine($"****************btnAsync_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// C#中的多线程
///Thread:.Netframework1.0 在.NetCore年代,依然支持;
///Thread:是C#语言对计算机资源线程操作的一个封装类;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThread_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnThread_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
{
//ThreadStart method = () =>
//{
// Thread.Sleep(3000);
// this.DoSomethingLong("btnThread_Click");
// Thread.Sleep(3000);
//};
//Thread thread = new Thread(method);
//thread.Start();
{
不靠谱,其实做不到马上停止、马上暂停
//thread.Suspend();//挂起、暂停
//thread.Resume(); //恢复暂停的线程
//thread.Abort();//停止 对外抛出ThreadAbortException异常
//Thread.ResetAbort();//让停止的线程继续允许
就可以通过判断状态来做到线程等待
1.等待
//while (thread.ThreadState!=ThreadState.Stopped)
//{
// Thread.Sleep(100);
//}
2.Join 等待
//thread.Join();//主线程等待子线程计算完成;卡界面
//thread.Join(2000);//等待2000ms,过时不候;
//thread.Priority = ThreadPriority.Normal; //线程优先级,其实是提高优先执行的概率;有意外;优先执行并不代表优先结束; 千万不要用这个来控制线程的执行顺序;
// thread.IsBackground = true;//后台线程;进程关闭,线程也就消失了
//thread.IsBackground = false; //前台线程:进程关闭,线程执行完计算才消失
}
//{
// ParameterizedThreadStart mehtod = s =>
// {
// Console.WriteLine(s.ToString());
// this.DoSomethingLong("btnThread_Click");
// };
// Thread thread1 = new Thread(mehtod);
// thread1.Start("Hugh");
//}
//如果想要控制线程顺序呢?回调;其实是第一个线程里的动作执行完毕以后,去执行第二个动作呗;
//1.其实是第一个线程里的动作执行完毕以后,去执行第二个动作呗;
//2.既然是子线程来执行:必然是异步的,不能卡界面 ;
//扩展封装一个;
//{
// ThreadStart method = () =>
// {
// Thread.Sleep(3000);
// Console.WriteLine("欢迎大家来到.Net高级班。。");
// Thread.Sleep(3000);
// };
// Action actionCallBack = () => {
// Console.WriteLine("大家来的挺齐;");
// };
// actionCallBack += () => {
// Console.WriteLine("这是多播委托的。。。");
// };
// this.ThreadWithCallBack(method, actionCallBack);
//}
//那如果需要返回值呢?
{
//2020
Func<int> func = () =>
{
Thread.Sleep(3000);
return DateTime.Now.Year;
};
//int iResult = this.ThreadWithReturen<int>(func);
//能得到2020这个结果吗?
Func<int> func1Rusult = this.ThreadWithReturen<int>(func); //不卡界面
{
Console.WriteLine("");
Console.WriteLine("这里的执行也需要3秒钟。。。");
}
int iRsult = func1Rusult.Invoke(); //这里会卡界面
}
}
Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
/// <summary>
/// 控制线程顺序
/// //两个委托封装回调
/// </summary>
/// <param name="threadStart"></param>
/// <param name="actionCallBack"></param>
private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallBack)
{
//1.While 死循环 判断状态 等待
//2.Join
//Thread thread = new Thread(threadStart);
//thread.Start();
{
while (thread.ThreadState != ThreadState.Stopped)
{
Thread.Sleep(100);
}
actionCallBack.Invoke();
}
//{
// thread.Join(); //卡界面
// actionCallBack.Invoke();
//}
ThreadStart method = () =>
{
threadStart.Invoke();
actionCallBack.Invoke();
};
Thread thread = new Thread(method);
thread.Start();
}
/// <summary>
/// 一个新的线程来执行委托里的动作
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private Func<T> ThreadWithReturen<T>(Func<T> func)
{
#region MyRegion
1.既需要异步执行,
2.又需要有得到计算结果;
根本做不到;
//T t = default(T);
//ThreadStart threadStart = () =>
//{
// t = func.Invoke();
//};
//Thread thread = new Thread(threadStart);
//thread.Start();
//thread.Join();//如果在这里Join,卡界面了呢?
//return t;
1. 回调?
2.Join、
#endregion
//花式玩法:
//返回一个委托
T t = default(T);
ThreadStart threadStart = () =>
{
t = func.Invoke();
};
Thread thread = new Thread(threadStart);
thread.Start();//不卡界面
return new Func<T>(() =>
{
thread.Join();
return t;
});
}
2.ThreadPool
/// <summary>
/// 线程池
/// 在.Netframwork2.0基于Thread做了个升级;
/// Thread功能很强大,但是让开发者用不好;
/// Thread:框架没有去控制线程数量;容易滥用,就疯狂开启线程数量,其实在开发中,业务处理不好,开线程就让 服务器扛不住;
/// 就像给一个小孩儿一把热武器,不仅用不了,反而还会造成自身的伤害;
/// 池化思想:
// 如果某个对象创建和销毁代价比较高,同时这个对象还可以反复使用的,就需要一个池子
// 保存多个这样的对象,需要用的时候从池子里面获取;用完之后不用销毁,放回池子;(享元模式)
/// 如何去分配一个线程;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadpool_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnThreadpool_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//1.如何分配一个线程;
{
//WaitCallback waitCallback = o =>
//{
// Console.WriteLine(o);
// this.DoSomethingLong("btnThreadpool_Click");
//};
//ThreadPool.QueueUserWorkItem(waitCallback);
//ThreadPool.QueueUserWorkItem(waitCallback,"欢迎大家");
}
{
2.设置线程池中线程的数量
//Console.WriteLine("*********************************************");
//ThreadPool.SetMinThreads(2, 2);
//ThreadPool.SetMaxThreads(4, 4); //设置的最大值,不能小于计算机的逻辑线程数
//ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads);
//Console.WriteLine($"Min this minworkerThreads={minworkerThreads} this mincompletionPortThreads={mincompletionPortThreads}");
//ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads);
//Console.WriteLine($"Min this maxworkerThreads={maxworkerThreads} this maxcompletionPortThreads={maxcompletionPortThreads}");
线程池里的线程我们可以设置,但是不要随便折腾;因为设置以后,线程相当于当前进程而言,是全局的;
Task Parallel都是来自于线程池; 但是new Thread 又可以新开一个线程;会占一个线程池的位置;
}
{
3.线程等待; 观望式;
ManualResetEvent 默认要求参数状态false-----关闭-----mre.Set();--打开
ManualResetEvent 状态参数true----打开---mre.Reset();-关闭
//ManualResetEvent mre = new ManualResetEvent(false);
//ThreadPool.QueueUserWorkItem(o =>
//{
// Console.WriteLine(o);
// this.DoSomethingLong("欢迎大家来到.Net高级班的。。。。");
// mre.Set();//状态false----ture
// mre.Reset();
//});
//Console.WriteLine("do some thing else");
//Console.WriteLine("do some thing else");
//Console.WriteLine("do some thing else");
//Console.WriteLine("do some thing else");
//mre.WaitOne();//只要是mre状态编程true;就继续往后执行;
//Console.WriteLine("任务全部完成了。。。。。");
}
{
ThreadPool.SetMaxThreads(16, 16);
ManualResetEvent mre = new ManualResetEvent(false);
for (int i = 0; i < 18; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem(t =>
{
Console.WriteLine($"ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")} {k}");
if (k == 17)
{
mre.Set();
}
else
{
mre.WaitOne();
}
});
}
if (mre.WaitOne()) //只有在mre.Set();执行以后,状态值为true,才能往后执行;
{
Console.WriteLine("所有任务执行完成。。。。。。");
}
}
Console.WriteLine($"****************btnThreadpool_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}