提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
留点东西,所有的观点均是自己实践,可能并不是很准确,仅供参考,有错误还望指出!谢谢!
各个类的用法什么的网上一大堆,就不写了。
一、Thread
这个类个人感觉是最接近底层的线程类,提供了许多强大的接口例如挂起,继续,终止等等接口
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
配合 thread.Abort() 可以实现直接Kill当前线程的效果
优点
可以非常迅速的从系统中申请线程,迅速运行较多的需要并行运行的任务(我的电脑测试出来的是同时2500个左右,多了会内存不够,程序崩溃。)
缺点
申请资源不受管控,一次性获取过多的线程会造成内存不足,程序崩溃等问题。
二、ThreadPool
ThreadPool类相当是给Thread的缺点打了个补丁。他会一次性申请几个线程给程序,当调用线程时就从里面取出一个空闲线程分配出来。当调用的线程运行完后又交还给线程池中。
可以通过 ThreadPool.SetMaxThreads (int workerThreads,int completionPortThreads)
设置线程池线程的默认个数。
优点
弥补了Thread的缺点
缺点
缺点就是 没有了Thread的优点 因为在这个池子机制的管控下,.net是有最小默认工作线程的,比如说我默认线程4个,那我这个时候要跑5个任务,这个时候系统就会等待前4个中的任何一个线程空闲下来才会执行第5个,或者等待0.5秒到1秒钟线程池会再从系统中申请一个线程来给新的任务分配线程,如果任务个数较多,那么对于执行完全部任务的速度将会大大降低。
题外话 写到这我就觉得就尼玛的离谱,好像真的没有完美的工具,但是工具死的,人是活的,合理的通过现有资源来开发出合适的项目才是正确的目标
三、Task
Thread 和 ThreadPool 的升级版,Task开的线程都是基于线程池的,同时他开线程很方便,而且在控制和拓展上提供的功能也比较多。
Task的启动有4种方式,分别是:实例化的方式+Start方法启动、Task.Run()方法启动、TaskFactory.StartNew()方法启动、Task.RunSynchronously() 是同步 启动。
Task下的线程等待和延续主要以下几类:
Wait:
针对单个Task的实例,可以task1.wait进行线程等待(如果主线程执行,则卡主线程)
WaitAny:
执行的线程等待其中任何一个线程执行完毕即可执行(如果主线程执行,则卡主线程)
WaitAll:
执行的线程等待其中所有线程执行完毕方可执行(如果主线程执行,则卡主线程)
WhenAny:
与下面ContinueWith配合执行,当传入的线程中任何一个线程执行完毕,继续执行ContinueWith中的任务(属于开启新线程,不卡主线程)
WhenAll:
与下面ContinueWith配合执行,当传入的线程中所有线程执行完毕,继续执行ContinueWith中的任务(属于开启新线程,不卡主线程)
ContinueWith:
和上面WhenAny和WhenAll配合使用
四、TaskFactory
给Task提供的一个工具类,可以方便的使用线程等待,以及创建新线程任务。
taskFactory.StartNew(); //等价于Task.Run()
taskFactory.ContinueWhenAny();
taskFactory.ContinueWhenAll();
详情
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => this.Coding("参数1")));
taskList.Add(taskFactory.StartNew(() => this.Coding("参数2")));
taskList.Add(Task.Run(() => this.Coding("参数3")));
taskList.Add(taskFactory.StartNew(() => this.Coding("参数4")));
taskList.Add(Task.Run(() => this.Coding("参数5")));
//当taskList中执行完任意一个任务时,非阻塞式的回调
taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
{
Console.WriteLine($"有一个任务执行完成");
});
//当taskList中执行完所有任务时,非阻塞式的回调
taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), rArray =>
{
Console.WriteLine($"所有任务执行完成");
}));
五、async 和 await
这玩意儿语法很反人类呀有木有?但是好处在于挺方便,不需要使用一堆的代码就可以实现不卡主进程的效果。
对比一下不用 await,async和用 await,async的差别
首先定义异步请求任务。
private async Task<int> AsyncFunction(int obj) //创建async异步任务
{
int val;
await Task.Run(() =>
{
val = 0;
Console.WriteLine("开始模拟业务请求");
TaskTestClass._DryRun();
Console.WriteLine("获取模拟业务请求返回值");
val = obj;
});
return val;
}
private Task<int> DfFunction(int obj) //创建常规异步任务
{
return Task<int>.Run(() =>
{
int val = 0;
Console.WriteLine("开始模拟业务请求");
TaskTestClass._DryRun();
Console.WriteLine("获取模拟业务请求返回值");
val = obj;
return val;
});
}
然后创建在调用函数
//使用 await
private async void AsyncAwaitTest()
{
var taskResult = await AsyncFunction(10); //主线程中同步等待任务执行结果,且不会卡主线程
this.Title = taskResult.ToString(); //等待任务完成后,主线程执行任务
}
//不使用 await
private void AsyncTest()
{
TaskFactory taskFactory = new TaskFactory();
taskFactory.ContinueWhenAny(new Task[] { DfFunction(10) }
//开始异步执行AsyncFunction(10)任务等待结果,异步当然也不会卡界面
, new Action<Task>((inTask) =>
{ //等待异步任务完成后
App.Current.Dispatcher.Invoke(() => //使用主线程执行任务
{
var taskResult = (inTask as Task<int>).Result;
this.Title = taskResult.ToString();
});
}));
}
结论:一样的效果,不一样的代码量,果然还是 await,async 方便
反编译完以后,发现里面有个线程调度的玩意儿,但我没去细看,应该就是这个东西实现的不卡界面的效果,先留个心结在这,有兴趣的时候再去细看。