目录

一、需求

二、基本的Task用法

三、让Task任务按顺序执行

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

五、异步任务队列按顺序执行

六、封装任务队列

七、封装任务队列优化版

结束


一、需求

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

二、基本的Task用法

新建一个Winfrom项目

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace 线程2
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){Task task1 = new Task(() =>{Thread.Sleep(400);Console.WriteLine("task1");});Task task2 = new Task(() =>{Thread.Sleep(300);Console.WriteLine("task2");});Task task3 = new Task(() =>{Thread.Sleep(200);Console.WriteLine("task3");});Task task4 = new Task(() =>{Thread.Sleep(100);Console.WriteLine("task4");});task1.Start();task2.Start();task3.Start();task4.Start();}}
}

运行:

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

三、让Task任务按顺序执行

修改代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace 线程2
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private List<Task> TaskList = new List<Task>();private void Form1_Load(object sender, EventArgs e){Task task1 = new Task(() =>{Thread.Sleep(400);Console.WriteLine("task1");});Task task2 = new Task(() =>{Thread.Sleep(300);Console.WriteLine("task2");});Task task3 = new Task(() =>{Thread.Sleep(200);Console.WriteLine("task3");});Task task4 = new Task(() =>{Thread.Sleep(100);Console.WriteLine("task4");});TaskList.Add(task1);TaskList.Add(task2);TaskList.Add(task3);TaskList.Add(task4);foreach (Task task in TaskList){task.Start();task.Wait();}}}
}

运行:

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

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

代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace 线程2
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private List<Task> TaskList = new List<Task>();private void Button_Calculate_Click(object sender, EventArgs e){Task task1 = new Task(async () =>{await Task.Delay(TimeSpan.FromSeconds(4));Console.WriteLine("task1");});Task task2 = new Task(async () =>{await Task.Delay(TimeSpan.FromSeconds(3));Console.WriteLine("task2");});Task task3 = new Task(async () =>{await Task.Delay(TimeSpan.FromSeconds(2));Console.WriteLine("task3");});Task task4 = new Task(async () =>{await Task.Delay(TimeSpan.FromSeconds(1));Console.WriteLine("task4");});TaskList.Add(task1);TaskList.Add(task2);TaskList.Add(task3);TaskList.Add(task4);foreach (Task task in TaskList){task.Start();task.Wait();}}}
}

运行:

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

五、异步任务队列按顺序执行

代码:

private void Test()
{Task.Run(() =>{Task t1 = new Task(() => {Thread.Sleep(2000);Console.WriteLine("t1");num = 1;});t1.Start();t1.Wait();Task t2 = new Task(() => {Thread.Sleep(1000);Console.WriteLine("t2");num = 3;});t2.Start();t2.Wait();Console.WriteLine("线程执行完毕");});
}

运行:

效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:

private async void Test()
{await Task.Run(async () =>{await Task.Delay(4000);Trace.WriteLine("第1个线程执行");});await Task.Run(async () =>{await Task.Delay(3000);Trace.WriteLine("第2个线程执行");});await Task.Run(async () =>{await Task.Delay(2000);Trace.WriteLine("第3个线程执行");});
}

运行:

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

六、封装任务队列

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

using System;
using System.Collections.Generic;
using System.Threading.Tasks;namespace Utils
{public class TaskQueue{/// <summary>/// 任务列表/// </summary>private List<Task> TaskList = null;/// <summary>/// 是否在执行任务中/// </summary>private bool isPerformTask = false;/// <summary>/// 执行完任务的回调/// </summary>public Action CallBack = null;private static TaskQueue _instance = null;public static TaskQueue Instance{get{if (_instance == null)_instance = new TaskQueue();return _instance;}}/// <summary>/// 添加任务/// </summary>/// <param name="task"></param>public void AddTask(Task task){if (isPerformTask){Console.WriteLine("[TaskQueue]任务正在执行中,此时不能做赋值操作");return;}if (task != null){TaskList.Add(task);}}/// <summary>/// 执行任务/// </summary>public void PerformTask(){if (isPerformTask){Console.WriteLine("[TaskQueue]任务正在执行中,不可重复调用");return;}if (TaskList == null || TaskList.Count == 0){Console.WriteLine("[TaskQueue]任务列表为空");return;}         Task.Run(() =>{isPerformTask = true;foreach (Task item in TaskList){item.Start();item.Wait();}TaskList.Clear();isPerformTask = false;if (CallBack != null) CallBack();});}private TaskQueue(){TaskList = new List<Task>();}}
}

调用:

Task task1 = new Task(() =>
{Thread.Sleep(1000);Console.WriteLine("t1");
});
Task task2 = new Task(() =>
{Thread.Sleep(2000);Console.WriteLine("t2");
});
Task task3 = new Task(() =>
{Console.WriteLine("t3");
});
Action callback = () =>
{Console.WriteLine("所有任务执行完成");
};
TaskQueue.Instance.AddTask(task1);
TaskQueue.Instance.AddTask(task2);
TaskQueue.Instance.AddTask(task3);
TaskQueue.Instance.CallBack = callback;
TaskQueue.Instance.PerformTask();

运行:

七、封装任务队列优化版

2022.07.26 优化,和上面版本的区别是,任务添加完成后,会自动调用。

执行任务队列,任务会一个个执行,中间可等待,并且不会阻塞主线程,在运行的时候,可以向任务队列中添加任务,不会影响任务的执行,

代码:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;namespace Utils
{public class TaskQueue{/// <summary>/// 任务队列/// </summary>private Queue<TaskData> QueuesTask = new Queue<TaskData>();/// <summary>/// 任务队列是否在执行中/// </summary>private bool isExecuteing = false;private static TaskQueue _instance = null;public static TaskQueue Instance{get{if (_instance == null)_instance = new TaskQueue();return _instance;}}/// <summary>/// 任务是否进行中/// </summary>/// <returns></returns>public bool IsTasking(){return isExecuteing;}/// <summary>/// 添加任务,任务会按照队列自动执行/// </summary>/// <param name="task"></param>public void AddTaskAndRuning(TaskData taskData){if (taskData == null) return;QueuesTask.Enqueue(taskData);StartPerformTask();} /// <summary>/// 执行任务/// </summary>private async void StartPerformTask(){if (isExecuteing) { return; }while (QueuesTask.Count > 0){isExecuteing = true;await Task.Run(() =>{TaskData taskDatas = QueuesTask.Dequeue();Task task = taskDatas.Tasks;task.Start();task.Wait();if (taskDatas.CallBack != null) taskDatas.CallBack(null);});}isExecuteing = false;}private TaskQueue(){}}public class TaskData{/// <summary>/// 任务名/// </summary>public string Name { get; set; }/// <summary>/// 任务/// </summary>public Task Tasks { get; set; }/// <summary>/// 任务完成后的回调/// </summary>public Action<string> CallBack { get; set; }}
}

调用:

TaskData taskData2 = new TaskData();
taskData2.Tasks = new Task(() =>
{Thread.Sleep(4000);Console.WriteLine("[Form1]taskData2");
});
taskData2.CallBack = (string res) =>
{Console.WriteLine("[Form1]taskData2执行完的回调");
};
TaskQueue.Instance.AddTaskAndRuning(taskData2);TaskData taskData3 = new TaskData();
taskData3.Tasks = new Task(() =>
{Thread.Sleep(5000);Console.WriteLine("[Form1]taskData3");
});
taskData3.CallBack = (string res) =>
{Console.WriteLine("[Form1]taskData3执行完的回调");
};
TaskQueue.Instance.AddTaskAndRuning(taskData3);

运行:

结束

如果这个帖子对你有用,欢迎给我点赞 + 留言,谢谢

end

C# Task任务队列相关推荐

  1. [译] 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  2. 深入Android内存泄露

    深入内存泄露 android应用层的内存泄露,其实就是java虚拟机的内存泄漏. (这里,暂不讨论C/C++本地内存的堆泄漏) 1.知识储备 1.Java内存模型 相关内存对象模型,参照博客精讲Jav ...

  3. php swoole process,Swoole_process实现进程池的方法

    Swoole的进程之间有两种通信方式,一种是消息队列(queue),另一种是管道(pipe),对swoole_process 的研究在swoole中显得尤为重要. IO多路复用 swoole 中的io ...

  4. 【移动开发】Android应用程序中实用的代码框架(一)

    好久没有更新博客了,这段时间里和我的小伙伴们("乌索普"."丁二爷"."小娜".'小雯'')参加了一个大学生Android应用软件比赛,利用 ...

  5. PHP实进程池,swoole_process实现进程池的方法示例

    swoole -- 重新定义PHP swoole 的进程之间有两种通信方式,一种是消息队列(queue),另一种是管道(pipe),对swoole_process 的研究在swoole中显得尤为重要. ...

  6. 使用pthread和线程池实现B+树的并行块加载bulkload过程

    数据库课程设计 实验环境 架构:Intel x86_64 (虚拟机) 操作系统:Ubuntu 20.04 汇编器:gas (GNU Assembler) in AT&T mode 编译器:gc ...

  7. 操作系统实验报告15:进程同步与互斥线程池

    操作系统实验报告15 实验内容 实验内容:进程同步. 内容1:编译运行课件 Lecture18 例程代码. Algorithms 18-1 ~ 18-9. 内容2:在 Lab Week 13 的基础上 ...

  8. 操作系统实验报告13:线程池简单实现

    操作系统实验报告13 实验内容 实验内容:设计实现一个线程池 (Thread Pool) 使用 Pthread API FIFO 先不考虑互斥问题 编译.运行.测试用例 实验环境 架构:Intel x ...

  9. Android内存泄漏总结

    Android 内存泄漏总结 箫鉴哥 2016-01-19 13:44:26 浏览42979 评论10 android 性能优化 阿里技术协会 内存管理 内存泄漏 摘要: Android 内存泄漏总结 ...

最新文章

  1. Exchange 2013与OWA13集成
  2. 从12月7日起.广东移动不再区分cmwap,cmnet流量!
  3. python高级开发面试题_python面试的100题(16)
  4. 人工智能带来的事业浪潮或将提前到来
  5. Make Even(800)
  6. rust服务器假人文件,rust游戏手机版-rust手游预约(腐蚀游戏手机版)-乐游网安卓...
  7. window10怎么卸载php,window_win10怎么卸载程序?win10卸载程序教程,当win10正式版发布以后,不少 - phpStudy...
  8. 【Flink】Flink 部分算子是 FinishSHED 不做checnpoint
  9. Android 蓝牙开发(2)——低功耗蓝牙
  10. 退火模拟算法c语言程序,C语言模拟退火算法(C language simulated annealing algorithm).doc...
  11. java 网络五子棋游戏_基于JAVA的网络五子棋游戏
  12. 查询结果按中文拼音顺序排序
  13. gmail smtp 535 5.7.8无法登陆解决
  14. 谷粒商城 - 项目环境搭建
  15. Linux中的ps指令详解
  16. 开关电源MOS管选型500V、600V、650V参数
  17. python自动生成采集规则_快速制作规则及获取规则提取器AP
  18. 浅谈BIM+智慧工地,拒绝吹嘘,拒绝高大上。
  19. 通达OA web页面与精灵显示内容更新后不一致的问题
  20. Java重定向标准输入/输出

热门文章

  1. Win10下findfont: Font family [‘sans-serif‘] not found中文显示问题解决方法
  2. 智合同丨你还在为填写合同台账犯愁吗?
  3. android canvans 画3d,如何用Canvas做一个3D球
  4. [EDA]8位双向移位寄存器的设计
  5. VmWare网络配置,只此一篇就够了
  6. 我的世界服务器自动掉tps,我的世界服务器无限崩溃重启
  7. 虚幻4 半透明材质做彩色玻璃、塑料盒性能对比
  8. 关于试装国产操作系统UOS以及一些想法壁纸分享
  9. python下载package失败_Python package install血泪史
  10. 苹果mac 连接服务器_每日新闻摘要:苹果从Mac移除Zoom的Web服务器