当前位置:   article > 正文

C# Winform 基于Task的异步与延时执行_c#winform基于task的异步与延时执行

c#winform基于task的异步与延时执行

一、Task的机制


Task位于命名空间System.Threading.Tasks中,是.NET 4.0加入的新模块,其实现机制大致类似于线程池ThreadPool,不过对于ThreadPool来说Task的优势是很明显的:

ThreadPool的实现机制:(一对多)

1、应用程序拥有一个用于存放委托的全局队列;
2、使用ThreadPool.QueueUserWorkItem将新的委托加入到全局队列;
3、线程池中的多个线程按照先进先出的方式取出委托并执行。

Task的实现机制:(多对多)

1、应用程序拥有一个用于存放Task(包装的委托)的全局队列(存放主程序创建的Task,标记为了TaskCreationOptions.PreferFairness的Task),以及线程池中每个工作线程对应的本地队列(存放该工作线程创建的Task);
2、使用new Task()或Task.Factory.StartNew将新的Task加入到指定队列;
3、线程池中的多个线程按照优先处理本地队列,其次处理全局队列的方式取出Task并执行;
4、如果工作线程A发现本地队列为空(Task已处理完毕),那么A就会尝试去全局队列中获取Task,如果全局队列也为空,那么A就会到工作线程B的本地队列中“窃取”一个Task来执行,这种策略很明显的使得CPU更加充分的利用了并行执行。



二、Task的使用


创建Task并运行:

  1. //新建一个Task
  2. Task t1 = new Task(() => {
  3. Console.WriteLine("Task完成!");
  4. });
  5. //启动Task
  6. t1.Start();
  7. Console.WriteLine("UI线程完成!");

上面是用new关键字创建,等同于如下使用Task.Factory(Task工厂)创建的方式:

  1. //新建一个Task(Task工厂创建,自动启动)
  2. Task t1 = Task.Factory.StartNew(() => {
  3. Console.WriteLine("Task完成!");
  4. });
  5. Console.WriteLine("UI线程完成!");

这里为了简便使用了Lambda表达式(=> 为Lambda运算符),上面两部分代码都等同于如下:

  1. void Test()
  2. {
  3. Console.WriteLine("Task完成!");
  4. }
  5. Action action = new Action(Test);
  6. //新建一个Task
  7. Task t1 = new Task(action);
  8. //启动Task
  9. t1.Start();
  10. Console.WriteLine("UI线程完成!");

运行效果图:



Task的执行方式有同步和异步两种,上面的方式很明显是异步执行,我们可以看到做为主线程的UI线程是先一步执行完的。

那么要怎么样才能实现Task的同步执行呢?主要就这一个方法:Wait()!

代码如下:

  1. //新建一个Task
  2. Task t1 = new Task(() => {
  3. Console.WriteLine("Task完成!");
  4. });
  5. //启动Task
  6. t1.Start();
  7. Console.WriteLine("UI线程开始等待!");//①
  8. t1.Wait();
  9. Console.WriteLine("UI线程完成!");//②

运行效果图:



主线程运行到t1.Wait()时,会读取t1的状态,当发现任务t1还未执行结束时,主线程便会阻塞在这个位置(只是阻塞在t1.Wait()位置,也就是说t1.Wait()之前的代码①照旧执行),当读取到t1的状态为已经执行结束时,主线程才会再次恢复执行,从t1.Wait()之后的位置②继续往下执行。

当然,当有多个任务都需要保持同步执行时,可以使用Task.WaitAll方法同时等待多个任务完成,代码如下:

  1. //新建一个Task
  2. Task t1 = new Task(() => {
  3. Console.WriteLine("Task1完成!");
  4. });
  5. //新建一个Task
  6. Task t2 = new Task(() => {
  7. Console.WriteLine("Task2完成!");
  8. });
  9. //启动Task
  10. t1.Start();
  11. t2.Start();
  12. Console.WriteLine("UI线程开始等待!");
  13. //等待t1,t2都完成
  14. Task.WaitAll(t1,t2);
  15. Console.WriteLine("UI线程完成!");

运行效果图:



当然,对于Task的操作还有更多,这里对于我的需求无关紧要,所以不再列举,详情请参见MSDN的API文档:



三、基于Task的异步与延时


我在这里进行了如下封装:

  1. /// <summary>
  2. /// 开始一个异步任务
  3. /// </summary>
  4. /// <param name="taskAction">异步任务执行委托</param>
  5. /// <param name="taskEndAction">异步任务执行完毕后的委托(会跳转回UI线程)</param>
  6. /// <param name="control">UI线程的控件</param>
  7. public void StartAsyncTask(Action taskAction, Action taskEndAction, Control control)
  8. {
  9. if (control == null)
  10. {
  11. return;
  12. }
  13. Task task = new Task(() => {
  14. try
  15. {
  16. taskAction();
  17. //返回UI线程
  18. control.Invoke(new Action(() =>
  19. {
  20. taskEndAction();
  21. }));
  22. }
  23. catch (Exception e)
  24. {
  25. MessageBox.Show(e.Message);
  26. }
  27. });
  28. task.Start();
  29. }
  30. /// <summary>
  31. /// 开始一个延时任务
  32. /// </summary>
  33. /// <param name="DelayTime">延时时长(秒)</param>
  34. /// <param name="taskEndAction">延时时间完毕之后执行的委托(会跳转回UI线程)</param>
  35. /// <param name="control">UI线程的控件</param>
  36. public void StartDelayTask(int DelayTime, Action taskEndAction, Control control)
  37. {
  38. if (control == null)
  39. {
  40. return;
  41. }
  42. Task task = new Task(() => {
  43. try
  44. {
  45. Thread.Sleep(DelayTime * 1000);
  46. //返回UI线程
  47. control.Invoke(new Action(() =>
  48. {
  49. taskEndAction();
  50. }));
  51. }
  52. catch (Exception e)
  53. {
  54. MessageBox.Show(e.Message);
  55. }
  56. });
  57. task.Start();
  58. }

StartAsyncTask主要是执行一个异步操作,并在异步操作完成后执行指定的委托,这里因为Task的执行机制依然是多线程,由于winform的线程安全性使得非UI线程无法访问UI线程中的UI控件,所以在Task操作结束后执行的委托有必要返回到UI线程中,也就是说StartAsyncTask主要的功能就是在taskAction中执行一系列的异步运算,运算结束之后在taskEndAction中进行一些可视化的表现,比如给某某UI控件赋值。

StartDelayTask几乎等同于StartAsyncTask,只不过他更加的表现出来一种延时的特性,事实上在StartAsyncTask的taskAction中加入线程Sleep也就是StartDelayTask的效果了。

StartAsyncTask的使用,异步耗时操作:

  1. //显示耗时等待界面(比如一串文字:正在加载,请稍等......)
  2. WaitPage.ShowWait();
  3. StartAsyncTask(
  4. () => {
  5. //进行耗时操作......
  6. },
  7. () => {
  8. //耗时操作完成,隐藏耗时等待界面
  9. WaitPage.HideWait();
  10. },
  11. this);

StartDelayTask的使用,异步延时等待:

  1. Console.WriteLine("我军将在三秒后发起反击!");
  2. StartDelayTask(
  3. //等待的秒数
  4. 3,
  5. () => {
  6. //等待结束要做的事
  7. Console.WriteLine("我军开始反击!");
  8. },
  9. this);
  10. for (int i = 0; i < 10; i++)
  11. {
  12. Console.WriteLine(String.Format("敌军第{0}轮进攻!",i));
  13. }

运行效果图:


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/104554
推荐阅读
相关标签
  

闽ICP备14008679号