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就是專(zhuān)門(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)目中,有專(zhuān)門(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#實(shí)現(xiàn)PDF按頁(yè)分割文件和分頁(yè)合并
iTextSharp 是一個(gè)開(kāi)源的 PDF 處理庫(kù),用于在 C# 程序中創(chuàng)建、編輯和處理 PDF 文件,本文將使用iTextSharp實(shí)現(xiàn)C# PDF分割與合并,感興趣的可以了解下2025-03-03
C#基礎(chǔ)語(yǔ)法:Base關(guān)鍵字學(xué)習(xí)筆記
這篇文章主要介紹了C#基礎(chǔ)語(yǔ)法:Base關(guān)鍵字學(xué)習(xí)筆記,本文講解了它的一些基礎(chǔ)知識(shí)以及測(cè)試代碼,需要的朋友可以參考下2015-06-06
Winform學(xué)生信息管理系統(tǒng)各子窗體剖析(3)
這篇文章主要針對(duì)Winform學(xué)生信息管理系統(tǒng)各子窗體進(jìn)行剖析,感興趣的小伙伴們可以參考一下2016-05-05
C#實(shí)現(xiàn)功能強(qiáng)大的中國(guó)農(nóng)歷日歷操作類(lèi)
這篇文章主要介紹了C#實(shí)現(xiàn)功能強(qiáng)大的中國(guó)農(nóng)歷日歷操作類(lèi),實(shí)例分析了C#操作時(shí)間及字符串的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03
C#獲得程序的根目錄以及判斷文件是否存在的實(shí)例講解
今天小編大家分享一篇C#獲得程序的根目錄以及判斷文件是否存在的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
C#中多維數(shù)組[,]和交錯(cuò)數(shù)組[][]的區(qū)別
這篇文章介紹了C#中多維數(shù)組[,]和交錯(cuò)數(shù)組[][]的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01

