分享

c#之task与thread区别及其使用

 眼镜蛇图书馆zh 2019-12-30

转载:

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. public void test()
  2. {
  3. var testTask = new Task(() =>
  4. {
  5. Console.WriteLine("task start");
  6. });
  7. testTask.Start();
  8. var factoryTeak = Task.Factory.StartNew(() =>
  9. {
  10. Console.WriteLine("factory task start");
  11. });
  12. }

2.我们来看看task的生命周期

  1. var testTask = new Task(() =>
  2. {
  3. Console.WriteLine("task start");
  4. System.Threading.Thread.Sleep(2000);
  5. });
  6. Console.WriteLine(testTask.Status);
  7. testTask.Start();
  8. Console.WriteLine(testTask.Status);
  9. Console.WriteLine(testTask.Status);
  10. testTask.Wait();
  11. Console.WriteLine(testTask.Status);
  12. Console.WriteLine(testTask.Status);

输出结果:

Created
task start
Running
Running
RanToCompletion
RanToCompletion

可以看出task确实是异步执行,并且wait很好地控制了task。

3.下面演示几个控制task的方法
   

  1.      var testTask = new Task(() =>
  2.             {
  3.                 Console.WriteLine("task start");
  4.                 System.Threading.Thread.Sleep(2000);
  5.             });
  6.   testTask.Start();
  7. testTask.Wait();


  1. var testTask = new Task(() =>
  2.             {
  3.               Console.WriteLine("task start");
  4.               System.Threading.Thread.Sleep(2000);
  5.             });
  6.   testTask.Start();
  7. var factoryTeak = Task.Factory.StartNew(() =>
  8.                {                 
  9.                   Console.WriteLine("factory task start");
  10.             });
  11.             Task.WaitAll(testTask, factoryTeak);
  12.             Console.WriteLine("end");


  1.  var testTask = new Task(() =>
  2.             {
  3.                 Console.WriteLine("task start");
  4.                 System.Threading.Thread.Sleep(2000);
  5.             });
  6.             testTask.Start();
  7.  var factoryTeak = Task.Factory.StartNew(() =>
  8.                {                  
  9.                   Console.WriteLine("factory task start");
  10.             });
  11.             Task.WaitAny(testTask, factoryTeak);
  12.             Console.WriteLine("end");

通过wait()对单个task进行等待,Task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。

4.task的回调执行

  1. var testTask = new Task(() =>
  2. {
  3. Console.WriteLine("task start");
  4. System.Threading.Thread.Sleep(2000);
  5. });
  6. testTask.Start();
  7. var resultTest = testTask.ContinueWith<string>((Task) => {
  8. Console.WriteLine("testTask end");
  9. return "end";
  10. });
  11. Console.WriteLine(resultTest.Result);

5.task的取消

首先创建一个取消task的令牌的实例,在不启动task直接取消:

  1. var tokenSource = new CancellationTokenSource();//创建取消task实例
  2. var testTask = new Task(() =>
  3. {
  4. for (int i = 0; i < 6; i++) {
  5. System.Threading.Thread.Sleep(1000);
  6. }
  7. },tokenSource.Token);
  8. Console.WriteLine(testTask.Status);
  9. tokenSource.Token.Register(()=> {
  10. Console.WriteLine("task is to cancel");
  11. });
  12. tokenSource.Cancel();
  13. Console.WriteLine(testTask.Status);

 输出结果:

Created
task is to cancel
Canceled

如果task启动了真的取消了task?

  1. var tokenSource = new CancellationTokenSource();//创建取消task实例
  2. var testTask = new Task(() =>
  3. {
  4. for (int i = 0; i <6; i++) {
  5. System.Threading.Thread.Sleep(1000);
  6. }
  7. },tokenSource.Token);
  8. Console.WriteLine(testTask.Status);
  9. testTask.Start();
  10. Console.WriteLine(testTask.Status);
  11. tokenSource.Token.Register(()=> {
  12. Console.WriteLine("task is to cancel");
  13. });
  14. tokenSource.Cancel();
  15. Console.WriteLine(testTask.Status);
  16. for (int i = 0; i < 10; i++)
  17. {
  18. System.Threading.Thread.Sleep(1000);
  19. Console.WriteLine(testTask.Status);
  20. }

输出结果:

Created
WaitingToRun
task is to cancel
Running
Running
Running
Running
Running
Running
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion

 可以看出其实并没有取消task,此时task还在继续跑。

6.task的嵌套

  1. var parentTask = new Task(()=> {
  2. var childTask = new Task(() =>{
  3. System.Threading.Thread.Sleep(2000);
  4. Console.WriteLine("childTask to start");
  5. });
  6. childTask.Start();
  7. Console.WriteLine("parentTask to start");
  8. });
  9. parentTask.Start();
  10. parentTask.Wait();
  11. Console.WriteLine("end");

此时为普通关联,父task和子task没影响


  1. var parentTask = new Task(()=> {
  2. var childTask = new Task(() =>{
  3. System.Threading.Thread.Sleep(2000);
  4. Console.WriteLine("childTask to start");
  5. }, TaskCreationOptions.AttachedToParent);
  6. childTask.Start();
  7. Console.WriteLine("parentTask to start");
  8. } );
  9. parentTask.Start();
  10. parentTask.Wait();
  11. Console.WriteLine("end");

此时为父task和子task关联,wait会一直等待父子task执行完。

6.task死锁的问题

我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了

7.对Spinlock的使用

举例来说Parallel.for和Parallel.foreach是线程不安全的,有可能达不到你的预期,此时就需要加锁来解决此问题,我们可以加lock和spinlock(自旋锁)来解决

  1. SpinLock slock = new SpinLock(false);
  2. var testLock= new object();
  3. long sum1 = 0;
  4. long sum2 = 0;
  5. long sum3 = 0;
  6. Parallel.For(0, 100000, i =>
  7. {
  8. sum1 += i;
  9. });

  10. Parallel.For(0, 100000, i =>
  11. {
  12. bool lockTaken = false;
  13. try
  14. {
  15. slock.Enter(ref lockTaken);
  16. sum2 += i;
  17. }
  18. finally
  19. {
  20. if (lockTaken)
  21. slock.Exit(false);
  22. }
  23. });
  24. Parallel.For(0, 100000, i =>
  25. {
  26. lock(testLock)
  27. {
  28. sum3 += i;
  29. };
  30. });
  31. Console.WriteLine("Num1的值为:{0}", sum1);
  32. Console.WriteLine("Num2的值为:{0}", sum2);
  33. 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:

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多