1.什么是thread
当我们提及多线程的时候会想到thread和threadpool,这都是异步操作,threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源。thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反。
总结:threadpool确实比thread性能优,但是两者都没有很好的api区控制,如果线程执行无响应就只能等待结束,从而诞生了task任务。
2.什么是task
task简单地看就是任务,那和thread有什么区别呢?Task的背后的实现也是使用了线程池线程,但它的性能优于ThreadPoll,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。同时Task提供了丰富的API来管理线程、控制。但是相对前面的两种耗内存,Task依赖于CPU对于多核的CPU性能远超前两者,单核的CPU三者的性能没什么差别。
3.创建一个task任务有两种模式:
1.使用factory创建会直接执行,使用new创建不会执行,必须等到start启动之后才执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public void test() { var testTask = new Task(() => { Console.WriteLine( "task start" ); }); testTask.Start(); var factoryTeak = Task.Factory.StartNew(() => { Console.WriteLine( "factory task start" ); }); } |
2.我们来看看task的生命周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var testTask = new Task(() => { Console.WriteLine( "task start" ); System.Threading.Thread.Sleep(2000); }); Console.WriteLine(testTask.Status); testTask.Start(); Console.WriteLine(testTask.Status); Console.WriteLine(testTask.Status); testTask.Wait(); Console.WriteLine(testTask.Status); Console.WriteLine(testTask.Status); |
输出结果:
Created
task start
Running
Running
RanToCompletion
RanToCompletion
可以看出task确实是异步执行,并且wait很好地控制了task。
3.下面演示几个控制task的方法
1
2
3
4
5
6
7
|
var testTask = new Task(() => { Console.WriteLine( "task start" ); System.Threading.Thread.Sleep(2000); }); testTask.Start(); testTask.Wait(); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var testTask = new Task(() => { Console.WriteLine( "task start" ); System.Threading.Thread.Sleep(2000); }); testTask.Start(); var factoryTeak = Task.Factory.StartNew(() => { Console.WriteLine( "factory task start" ); }); Task.WaitAll(testTask, factoryTeak); Console.WriteLine( "end" ); |
1
2
3
4
5
6
7
8
9
10
11
12
|
var testTask = new Task(() => { Console.WriteLine( "task start" ); System.Threading.Thread.Sleep(2000); }); testTask.Start(); var factoryTeak = Task.Factory.StartNew(() => { Console.WriteLine( "factory task start" ); }); Task.WaitAny(testTask, factoryTeak); Console.WriteLine( "end" ); |
通过wait()对单个task进行等待,Task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。
4.task的回调执行
1
2
3
4
5
6
7
8
9
10
11
|
var testTask = new Task(() => { Console.WriteLine( "task start" ); System.Threading.Thread.Sleep(2000); }); testTask.Start(); var resultTest = testTask.ContinueWith< string >((Task) => { Console.WriteLine( "testTask end" ); return "end" ; }); Console.WriteLine(resultTest.Result); |
5.task的取消
首先创建一个取消task的令牌的实例,在不启动task直接取消:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var tokenSource = new CancellationTokenSource(); //创建取消task实例 var testTask = new Task(() => { for ( int i = 0; i < 6; i++) { System.Threading.Thread.Sleep(1000); } },tokenSource.Token); Console.WriteLine(testTask.Status); tokenSource.Token.Register(()=> { Console.WriteLine( "task is to cancel" ); }); tokenSource.Cancel(); Console.WriteLine(testTask.Status); |
输出结果:
Created
task is to cancel
Canceled
如果task启动了真的取消了task?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var tokenSource = new CancellationTokenSource(); //创建取消task实例 var testTask = new Task(() => { for ( int i = 0; i <6; i++) { System.Threading.Thread.Sleep(1000); } },tokenSource.Token); Console.WriteLine(testTask.Status); testTask.Start(); Console.WriteLine(testTask.Status); tokenSource.Token.Register(()=> { Console.WriteLine( "task is to cancel" ); }); tokenSource.Cancel(); Console.WriteLine(testTask.Status); for ( int i = 0; i < 10; i++) { System.Threading.Thread.Sleep(1000); Console.WriteLine(testTask.Status); } |
输出结果:
Created
WaitingToRun
task is to cancel
Running
Running
Running
Running
Running
Running
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
可以看出其实并没有取消task,此时task还在继续跑。
6.task的嵌套
1
2
3
4
5
6
7
8
9
10
11
|
var parentTask = new Task(()=> { var childTask = new Task(() =>{ System.Threading.Thread.Sleep(2000); Console.WriteLine( "childTask to start" ); }); childTask.Start(); Console.WriteLine( "parentTask to start" ); }); parentTask.Start(); parentTask.Wait(); Console.WriteLine( "end" ); |
此时为普通关联,父task和子task没影响
1
2
3
4
5
6
7
8
9
10
11
|
var parentTask = new Task(()=> { var childTask = new Task(() =>{ System.Threading.Thread.Sleep(2000); Console.WriteLine( "childTask to start" ); }, TaskCreationOptions.AttachedToParent); childTask.Start(); Console.WriteLine( "parentTask to start" ); } ); parentTask.Start(); parentTask.Wait(); Console.WriteLine( "end" ); |
此时为父task和子task关联,wait会一直等待父子task执行完。
6.task死锁的问题
我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了
7.对Spinlock的使用
举例来说Parallel.for和Parallel.foreach是线程不安全的,有可能达不到你的预期,此时就需要加锁来解决此问题,我们可以加lock和spinlock(自旋锁)来解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
SpinLock slock = new SpinLock( false ); var testLock= new object (); long sum1 = 0; long sum2 = 0; long sum3 = 0; Parallel.For(0, 100000, i => { sum1 += i; }); Parallel.For(0, 100000, i => { bool lockTaken = false ; try { slock.Enter( ref lockTaken); sum2 += i; } finally { if (lockTaken) slock.Exit( false ); } }); Parallel.For(0, 100000, i => { lock (testLock) { sum3 += i; }; }); Console.WriteLine( "Num1的值为:{0}" , sum1); Console.WriteLine( "Num2的值为:{0}" , sum2); Console.WriteLine( "Num3的值为:{0}" , sum3); |
输出结果:
Num1的值为:1660913202
Num2的值为:4999950000
Num3的值为:4999950000
Num1的值为:2754493646
Num2的值为:4999950000
Num3的值为:4999950000
Num1的值为:4999950000
Num2的值为:4999950000
Num3的值为:4999950000
最后看看threadpoll和task的结构图:
threadpool:
task:
原文链接:https://blog.csdn.net/qq_40677590/article/details/102797838