C#如何Task執(zhí)行任務(wù),等待任務(wù)完成
Task執(zhí)行任務(wù),等待任務(wù)完成
代碼:
//任務(wù) Func<int> Funcs = () => { ? ? Console.WriteLine("任務(wù)開(kāi)始"); ?? ?return 1 + 1; }; ? //執(zhí)行任務(wù) Task<int> printRes = Task.Run(Funcs); ? //等待任務(wù)完成 printRes.GetAwaiter().OnCompleted(() => { ? ? Console.WriteLine("異步執(zhí)行結(jié)果:" + printRes.Result); ? ? ? ? });
運(yùn)行:
任務(wù)開(kāi)始
異步執(zhí)行結(jié)果:2
C# Task任務(wù)隊(duì)列
需求
眾所周知,方法體內(nèi)代碼是從上往下執(zhí)行的,在我們工作中經(jīng)常會(huì)遇到一些需要延時(shí)執(zhí)行,但又必須按順序來(lái)執(zhí)行的需求。這要怎么解決呢。微軟官方提供的Task API就是專門(mén)來(lái)解決這個(gè)問(wèn)題的。那么下面就開(kāi)始吧。
基本的Task用法
新建一個(gè)Winfrom項(xiàng)目
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(); } } }
運(yùn)行:
由于各個(gè)任務(wù)內(nèi)部延時(shí)不同,最先執(zhí)行的Task1,反而最后一個(gè)執(zhí)行完,如果既要做延時(shí)操作,又要求從任務(wù)按順序執(zhí)行,要怎么解決呢?
讓Task任務(wù)按順序執(zhí)行
修改代碼:
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(); } } } }
運(yùn)行:
用上面的方法雖然有效,你可以看看,點(diǎn)擊界面的時(shí)候,界面處鼠標(biāo)指針會(huì)一直轉(zhuǎn)圈,導(dǎo)致winfrom界面卡住,無(wú)法操作,這是因?yàn)槭褂肨hread.Sleep 導(dǎo)致主線程阻塞,下面就來(lái)解決UI界面卡死的問(wèn)題。
使用異步委托解決UI界面卡死問(wèn)題
代碼:
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(); } } } }
運(yùn)行:
用異步方式雖然界面不會(huì)卡住,但另一個(gè)問(wèn)題來(lái)了,task.wait()方法似乎沒(méi)有效果。里面的任務(wù)隊(duì)列依然沒(méi)有按順序來(lái)執(zhí)行。那么如何即使用異步執(zhí)行,也不阻塞主線程,而且要任務(wù)按順序來(lái)執(zhí)行呢?
異步任務(wù)隊(duì)列按順序執(zhí)行
代碼:
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("線程執(zhí)行完畢"); }); }
運(yùn)行:
效果是實(shí)現(xiàn)了,代碼看起來(lái)好搓啊,強(qiáng)迫癥都犯了,沒(méi)關(guān)系,可以使用更優(yōu)雅的寫(xiě)法:
private async void Test() { await Task.Run(async () => { await Task.Delay(4000); Trace.WriteLine("第1個(gè)線程執(zhí)行"); }); await Task.Run(async () => { await Task.Delay(3000); Trace.WriteLine("第2個(gè)線程執(zhí)行"); }); await Task.Run(async () => { await Task.Delay(2000); Trace.WriteLine("第3個(gè)線程執(zhí)行"); }); }
運(yùn)行:
到此為止,功能就實(shí)現(xiàn)了,這個(gè)需求在Unity3d中使用協(xié)程很簡(jiǎn)單的幾句就可以搞定,但在Winfrom等項(xiàng)目的開(kāi)發(fā)中,確實(shí)有點(diǎn)繁瑣。
封裝任務(wù)隊(duì)列
下面的代碼我不認(rèn)為是一個(gè)很好的寫(xiě)法,需要添加任務(wù)后,還得手動(dòng)去調(diào)用,如果能添加到任務(wù)隊(duì)列就不管了,讓其自己自動(dòng)按順序來(lái)執(zhí)行任務(wù),豈不是更好,讀者如果有興趣自己去完善這個(gè)猜想。另外,在游戲開(kāi)發(fā)中,比如RGP項(xiàng)目中,有專門(mén)的任務(wù)系統(tǒng),它和我這個(gè)帖子的概念不能混為一談,RPG任務(wù)系統(tǒng)更多的偏向數(shù)據(jù)的存取,來(lái)獲取任務(wù)的完成狀態(tài)。
using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Utils { public class TaskQueue { /// <summary> /// 任務(wù)列表 /// </summary> private List<Task> TaskList = null; /// <summary> /// 是否在執(zhí)行任務(wù)中 /// </summary> private bool isPerformTask = false; /// <summary> /// 執(zhí)行完任務(wù)的回調(diào) /// </summary> public Action CallBack = null; private static TaskQueue _instance = null; public static TaskQueue Instance { get { if (_instance == null) _instance = new TaskQueue(); return _instance; } } /// <summary> /// 添加任務(wù) /// </summary> /// <param name="task"></param> public void AddTask(Task task) { if (isPerformTask) { Console.WriteLine("[TaskQueue]任務(wù)正在執(zhí)行中,此時(shí)不能做賦值操作"); return; } if (task != null) { TaskList.Add(task); } } /// <summary> /// 執(zhí)行任務(wù) /// </summary> public void PerformTask() { if (isPerformTask) { Console.WriteLine("[TaskQueue]任務(wù)正在執(zhí)行中,不可重復(fù)調(diào)用"); return; } if (TaskList == null || TaskList.Count == 0) { Console.WriteLine("[TaskQueue]任務(wù)列表為空"); 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>(); } } }
調(diào)用:
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("所有任務(wù)執(zhí)行完成"); }; TaskQueue.Instance.AddTask(task1); TaskQueue.Instance.AddTask(task2); TaskQueue.Instance.AddTask(task3); TaskQueue.Instance.CallBack = callback; TaskQueue.Instance.PerformTask();
運(yùn)行:
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C# 使用SharpZipLib生成壓縮包的實(shí)例代碼
SharpZipLib是一個(gè)C#的類庫(kù),主要用來(lái)解壓縮Zip,GZip,BZip2,Tar等格式,是以托管程序集的方式實(shí)現(xiàn),可以方便的應(yīng)用于其他的項(xiàng)目之中。本文通過(guò)一個(gè)實(shí)例代碼給大家介紹了C# 使用SharpZipLib生成壓縮包的方法,感興趣的朋友跟隨小編一起看看吧2018-09-09c#之圓形無(wú)標(biāo)題欄橢圓窗體的實(shí)現(xiàn)詳解
本篇文章是對(duì)c#中圓形無(wú)標(biāo)題欄橢圓窗體的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06C#遍歷文件夾及其子目錄的完整實(shí)現(xiàn)方法
這篇文章主要介紹了C#遍歷文件夾及其子目錄的方法,涉及C#文件與目錄的基本操作技巧,簡(jiǎn)單實(shí)用,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06