Thread(线程)
Thread t = new Thread(Run);
t.Start();问:每个请求过来都创建一个线程?
答:不合适,大量占用内存,并且需要自己维护各个线程,麻烦!于是有了线程池=用于统一调度管理线程。
ThreadPool(线程池)
ThreadPool.QueueUserWorkItem(m => { Run(); });
线程池有会将已创建且空闲线程重复利用起来,10个请求也可能重复使用3个线程即可高效完成。
问:线程池是根据请求随意产生并分配线程数量的,如何控制?
答:使用信号量控制。
Semaphore(信号量)
Semaphore负责协调线程,可以限制对某一资源访问的线程数量。
SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三个线程同时访问
for (int i = 0; i < 10; i++) { new Thread(Run).Start(); }public void Run() { semLim.Wait(); //todo... semLim.Release(); }结果:至多只有3个线程同时在跑。
Task(线程)
Task是.NET4.0加入的,跟线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。
调用方法如下:
1、Task.Run()
2、Task.Factory.StartNew()
问:一个方法有10个步骤,第1步开启线程执行,继续执行2-9步,第10步需要等待第1步的完成才能执行,如何做到?
答:在第10步之前使用wait方法即可。
问:Thread和Task区别?
答:Thread就是跟线程打交道,Task是基于ThreadPool之上的跟线程池打交道,所以task优于thread,能充分利用空闲的线程继续干活以节省开支。
Task<TResult>
普通版:
Task task = Task.Run(() => { //todo... }); 带返回值版:Task<string> task = Task<string>.Run(() => { //todo... return "优秀"; }); 问:如何取线程返回值? 答:task.Result,需要注意的是这个是需要线程等待的,所以也要考虑线程阻塞问题。
Async/Await
async/await是C#5.0中推出的,其实就是基于task的语法糖。
他们是成对出现的!!
1.一个带返回值的异步方法。
private Task<string> GetString() { return Task<string>.Run(() => { Thread.Sleep(2000); return "优秀"; }); } 2.成对标识式的调用异步方法
public async Task<int> GetStrLengthAsync() { //此处返回的string中的字符串类型,而不是Task<string> string str = await GetString(); return str.Length; } 3.主线程中直接调用此标识方法public void Main(string[] args) { Task<int> task = GetStrLengthAsync(); }
ParallelParallel同样也是基于task,很好的解决循环体内的异步问题!
可以参考我之前的一篇博客:http://www.camelcat.com/2019/1126.html
需要注意的:循环体内如果需要对同一个对象进行写操作,一定要使用线程安全的方式;要么加锁,要么使用天然线程安全的数据结构(比如ConcurrentDictionary)。