设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间06 现在时间06 缓存数据 做一个积极向上的人,读温柔的句子,见阳光的人,眼里全是温柔和笑意。

做一个积极向上的人,读温柔的句子,见阳光的人,眼里全是温柔和笑意。

查看: 1403|回复: 3

C#如何Task执行任务,等待任务完成

[复制链接]

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:29
  • 打卡月天数:1
  • 打卡总奖励:346
  • 最近打卡:2025-07-03 14:04:48
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
367
主题
331
精华
0
金钱
1425
积分
758
注册时间
2023-2-10
最后登录
2025-7-3

发表于 2023-2-26 02:51:57 来自手机 | 显示全部楼层 |阅读模式
Task执行任务,等待任务完成

代码:
  1. //任务
  2. Func<int> Funcs = () =>
  3. {
  4.     Console.WriteLine("任务开始");
  5.     return 1 + 1;
  6. };
  7.  
  8. //执行任务
  9. Task<int> printRes = Task.Run(Funcs);
  10.  
  11. //等待任务完成
  12. printRes.GetAwaiter().OnCompleted(() =>
  13. {
  14.     Console.WriteLine("异步执行结果:" + printRes.Result);        
  15. });
复制代码
运行:
  1. 任务开始异步执行结果:2
复制代码
C# Task任务队列


需求

众所周知,方法体内代码是从上往下执行的,在我们工作中经常会遇到一些需要延时执行,但又必须按顺序来执行的需求。这要怎么解决呢。微软官方提供的Task API就是专门来解决这个问题的。那么下面就开始吧。

基本的Task用法

新建一个Winfrom项目
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using System.Windows.Forms;

  6. namespace 线程2
  7. {
  8.     public partial class Form1 : Form
  9.     {
  10.         public Form1()
  11.         {
  12.             InitializeComponent();
  13.         }

  14.         private void Form1_Load(object sender, EventArgs e)
  15.         {
  16.             Task task1 = new Task(() =>
  17.             {
  18.                 Thread.Sleep(400);
  19.                 Console.WriteLine("task1");
  20.             });
  21.             Task task2 = new Task(() =>
  22.             {
  23.                 Thread.Sleep(300);
  24.                 Console.WriteLine("task2");
  25.             });
  26.             Task task3 = new Task(() =>
  27.             {
  28.                 Thread.Sleep(200);
  29.                 Console.WriteLine("task3");
  30.             });
  31.             Task task4 = new Task(() =>
  32.             {
  33.                 Thread.Sleep(100);
  34.                 Console.WriteLine("task4");
  35.             });
  36.             task1.Start();
  37.             task2.Start();
  38.             task3.Start();
  39.             task4.Start();
  40.         }
  41.     }
  42. }
复制代码
运行:
035235zljzjdpwjqqbljro.jpeg

由于各个任务内部延时不同,最先执行的Task1,反而最后一个执行完,如果既要做延时操作,又要求从任务按顺序执行,要怎么解决呢?

让Task任务按顺序执行

修改代码:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using System.Windows.Forms;

  6. namespace 线程2
  7. {
  8.     public partial class Form1 : Form
  9.     {
  10.         public Form1()
  11.         {
  12.             InitializeComponent();
  13.         }

  14.         private List<Task> TaskList = new List<Task>();

  15.         private void Form1_Load(object sender, EventArgs e)
  16.         {
  17.             Task task1 = new Task(() =>
  18.             {
  19.                 Thread.Sleep(400);
  20.                 Console.WriteLine("task1");
  21.             });
  22.             Task task2 = new Task(() =>
  23.             {
  24.                 Thread.Sleep(300);
  25.                 Console.WriteLine("task2");
  26.             });
  27.             Task task3 = new Task(() =>
  28.             {
  29.                 Thread.Sleep(200);
  30.                 Console.WriteLine("task3");
  31.             });
  32.             Task task4 = new Task(() =>
  33.             {
  34.                 Thread.Sleep(100);
  35.                 Console.WriteLine("task4");
  36.             });

  37.             TaskList.Add(task1);
  38.             TaskList.Add(task2);
  39.             TaskList.Add(task3);
  40.             TaskList.Add(task4);

  41.             foreach (Task task in TaskList)
  42.             {
  43.                 task.Start();
  44.                 task.Wait();
  45.             }
  46.         }
  47.     }
  48. }
复制代码
运行:
035235oeeszczl0b777e7o.jpeg

用上面的方法虽然有效,你可以看看,点击界面的时候,界面处鼠标指针会一直转圈,导致winfrom界面卡住,无法操作,这是因为使用Thread.Sleep 导致主线程阻塞,下面就来解决UI界面卡死的问题。

使用异步委托解决UI界面卡死问题

代码:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using System.Windows.Forms;

  6. namespace 线程2
  7. {
  8.     public partial class Form1 : Form
  9.     {
  10.         public Form1()
  11.         {
  12.             InitializeComponent();
  13.         }


  14.         private List<Task> TaskList = new List<Task>();


  15.         private void Button_Calculate_Click(object sender, EventArgs e)
  16.         {
  17.             Task task1 = new Task(async () =>
  18.             {
  19.                 await Task.Delay(TimeSpan.FromSeconds(4));
  20.                 Console.WriteLine("task1");
  21.             });
  22.             Task task2 = new Task(async () =>
  23.             {
  24.                 await Task.Delay(TimeSpan.FromSeconds(3));
  25.                 Console.WriteLine("task2");
  26.             });
  27.             Task task3 = new Task(async () =>
  28.             {
  29.                 await Task.Delay(TimeSpan.FromSeconds(2));
  30.                 Console.WriteLine("task3");
  31.             });
  32.             Task task4 = new Task(async () =>
  33.             {
  34.                 await Task.Delay(TimeSpan.FromSeconds(1));
  35.                 Console.WriteLine("task4");
  36.             });

  37.             TaskList.Add(task1);
  38.             TaskList.Add(task2);
  39.             TaskList.Add(task3);
  40.             TaskList.Add(task4);

  41.             foreach (Task task in TaskList)
  42.             {
  43.                 task.Start();
  44.                 task.Wait();
  45.             }
  46.         }
  47.     }
  48. }
复制代码
运行:
035235yyjpwwtfofffvzwf.jpeg

用异步方式虽然界面不会卡住,但另一个问题来了,task.wait()方法似乎没有效果。里面的任务队列依然没有按顺序来执行。那么如何即使用异步执行,也不阻塞主线程,而且要任务按顺序来执行呢?

异步任务队列按顺序执行

代码:
  1. private void Test()
  2. {
  3.     Task.Run(() =>
  4.     {
  5.         Task t1 = new Task(() => {
  6.             Thread.Sleep(2000);
  7.             Console.WriteLine("t1");
  8.             num = 1;
  9.         });
  10.         t1.Start();
  11.         t1.Wait();
  12.         Task t2 = new Task(() => {
  13.             Thread.Sleep(1000);
  14.             Console.WriteLine("t2");
  15.             num = 3;
  16.         });
  17.         t2.Start();
  18.         t2.Wait();
  19.         Console.WriteLine("线程执行完毕");
  20.     });
  21. }
复制代码
运行:
035235uykmuiqomhhizh1f.jpeg

效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:
  1. private async void Test()
  2. {
  3.     await Task.Run(async () =>
  4.     {
  5.         await Task.Delay(4000);
  6.         Trace.WriteLine("第1个线程执行");
  7.     });
  8.     await Task.Run(async () =>
  9.     {
  10.         await Task.Delay(3000);
  11.         Trace.WriteLine("第2个线程执行");
  12.     });
  13.     await Task.Run(async () =>
  14.     {
  15.         await Task.Delay(2000);
  16.         Trace.WriteLine("第3个线程执行");
  17.     });
  18. }
复制代码
运行:
035235jpwyne31hnpluelw.jpeg

到此为止,功能就实现了,这个需求在Unity3d中使用协程很简单的几句就可以搞定,但在Winfrom等项目的开发中,确实有点繁琐。

封装任务队列

下面的代码我不认为是一个很好的写法,需要添加任务后,还得手动去调用,如果能添加到任务队列就不管了,让其自己自动按顺序来执行任务,岂不是更好,读者如果有兴趣自己去完善这个猜想。另外,在游戏开发中,比如RGP项目中,有专门的任务系统,它和我这个帖子的概念不能混为一谈,RPG任务系统更多的偏向数据的存取,来获取任务的完成状态。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;

  4. namespace Utils
  5. {
  6.     public class TaskQueue
  7.     {
  8.         /// <summary>
  9.         /// 任务列表
  10.         /// </summary>
  11.         private List<Task> TaskList = null;
  12.         /// <summary>
  13.         /// 是否在执行任务中
  14.         /// </summary>
  15.         private bool isPerformTask = false;
  16.         /// <summary>
  17.         /// 执行完任务的回调
  18.         /// </summary>
  19.         public Action CallBack = null;


  20.         private static TaskQueue _instance = null;
  21.         public static TaskQueue Instance
  22.         {
  23.             get
  24.             {
  25.                 if (_instance == null)
  26.                     _instance = new TaskQueue();
  27.                 return _instance;
  28.             }
  29.         }

  30.         /// <summary>
  31.         /// 添加任务
  32.         /// </summary>
  33.         /// <param name="task"></param>
  34.         public void AddTask(Task task)
  35.         {
  36.             if (isPerformTask)
  37.             {
  38.                 Console.WriteLine("[TaskQueue]任务正在执行中,此时不能做赋值操作");
  39.                 return;
  40.             }

  41.             if (task != null)
  42.             {
  43.                 TaskList.Add(task);
  44.             }
  45.         }

  46.         /// <summary>
  47.         /// 执行任务
  48.         /// </summary>
  49.         public void PerformTask()
  50.         {
  51.             if (isPerformTask)
  52.             {
  53.                 Console.WriteLine("[TaskQueue]任务正在执行中,不可重复调用");
  54.                 return;
  55.             }
  56.             if (TaskList == null || TaskList.Count == 0)
  57.             {
  58.                 Console.WriteLine("[TaskQueue]任务列表为空");
  59.                 return;
  60.             }         

  61.             Task.Run(() =>
  62.             {
  63.                 isPerformTask = true;

  64.                 foreach (Task item in TaskList)
  65.                 {
  66.                     item.Start();
  67.                     item.Wait();
  68.                 }

  69.                 TaskList.Clear();
  70.                 isPerformTask = false;

  71.                 if (CallBack != null) CallBack();
  72.             });
  73.         }

  74.         private TaskQueue()
  75.         {
  76.             TaskList = new List<Task>();
  77.         }
  78.     }
  79. }
复制代码
调用:
  1. Task task1 = new Task(() =>
  2. {
  3.     Thread.Sleep(1000);
  4.     Console.WriteLine("t1");
  5. });
  6. Task task2 = new Task(() =>
  7. {
  8.     Thread.Sleep(2000);
  9.     Console.WriteLine("t2");
  10. });
  11. Task task3 = new Task(() =>
  12. {
  13.     Console.WriteLine("t3");
  14. });
  15. Action callback = () =>
  16. {
  17.     Console.WriteLine("所有任务执行完成");
  18. };
  19. TaskQueue.Instance.AddTask(task1);
  20. TaskQueue.Instance.AddTask(task2);
  21. TaskQueue.Instance.AddTask(task3);
  22. TaskQueue.Instance.CallBack = callback;
  23. TaskQueue.Instance.PerformTask();
复制代码
运行:
035235bf6dm606rpzoamfo.jpeg

以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
15
积分
10
注册时间
2022-12-25
最后登录
2022-12-25

发表于 2024-11-2 03:08:06 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:无名新人
  • 打卡总天数:1
  • 打卡月天数:0
  • 打卡总奖励:39
  • 最近打卡:2024-07-06 00:15:39
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
53
积分
4
注册时间
2023-12-7
最后登录
2024-7-6

发表于 2025-1-15 13:45:04 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
11
积分
2
注册时间
2024-1-21
最后登录
2024-1-21

发表于 2025-6-25 05:26:36 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼
4楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表