網站首頁 編程語言 正文
Task執行任務,等待任務完成
代碼:
//任務
Func<int> Funcs = () =>
{
? ? Console.WriteLine("任務開始");
?? ?return 1 + 1;
};
?
//執行任務
Task<int> printRes = Task.Run(Funcs);
?
//等待任務完成
printRes.GetAwaiter().OnCompleted(() =>
{
? ? Console.WriteLine("異步執行結果:" + printRes.Result); ? ? ? ?
});
運行:
任務開始
異步執行結果:2
C# Task任務隊列
需求
眾所周知,方法體內代碼是從上往下執行的,在我們工作中經常會遇到一些需要延時執行,但又必須按順序來執行的需求。這要怎么解決呢。微軟官方提供的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();
運行:
原文鏈接:https://blog.csdn.net/qq_38693757/article/details/119882627
相關推薦
- 2022-07-20 Flutter系統網絡圖片加載流程解析_Android
- 2022-08-22 python作圖基礎之plt.contour實例詳解_python
- 2022-06-02 Python導包模塊報錯的問題解決_python
- 2022-06-02 C++的智能指針你真的了解嗎_C 語言
- 2022-08-21 golang協程與線程區別簡要介紹_Golang
- 2023-01-29 switch多選擇結構、循環結構示例詳解_Swift
- 2022-12-21 iOS開發學習TableView展現一個list實例_IOS
- 2022-04-12 無法加載文件 ,因為在此系統中禁止執行腳本。
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支