C# Task.Run使用問題解決
最近遇到一個問題:使用一個Thread.Timer的定時器每隔250毫秒輸出一串數(shù)據(jù),與此同時,有其他幾個Task在往數(shù)據(jù)庫更新數(shù)據(jù),這個過程比較耗時。因此這個數(shù)據(jù)庫操作影響了定時器輸出數(shù)據(jù)的操作,導致輸出數(shù)據(jù)延時了。由此問題寫了一個Demo進行測試:
private void Form1_Load(object sender,EventArgs e)
{
TimerCallbacl svCallBack=BatchQuerySVData;
System.Threading.Timer _timer=new System.Threading.Timer(svCallBack,null,0,250);
}
public static async void BatchQuerySVData(object state)
{
await Task.Run(()=>{
Console.WriteLine(Index++);
Thread.Sleep(200);
});
}
private void button1_Clcik(object sender,EventArgs e)
{
for(int i=0;i<=2;i++)
{
Task.Run(()=>{
Thread.Sleep(1000*10);
});
}
}
如上,當一個定時器的Task每250毫秒輸出數(shù)據(jù)時,此時增加一個耗時的點擊事件的Task,當頻繁點擊按鈕時,定時器的Task輸出的數(shù)據(jù)會發(fā)生延遲。
由此了解了一下Task及線程池的運行機制:
Task的核心機制包含以下幾個關鍵組成部分:
任務調(diào)度器(TaskScheduler)
- 默認使用線程池任務調(diào)度器
- 負責任務的排隊和執(zhí)行
- 支持工作竊取算法提高CPU利用率
狀態(tài)管理
- 維護任務的生命周期狀態(tài)(Created、WaitingToRun、Running、Completed等)
- 通過狀態(tài)機管理異步操作的執(zhí)行流程
延續(xù)任務(Continuation)
- 使用
ContinueWith或await創(chuàng)建任務鏈 - 自動處理任務間的依賴關系
- 使用
線程池集成
- 重用線程池線程,避免頻繁創(chuàng)建銷毀線程
- 動態(tài)調(diào)整線程數(shù)量以適應負載變化
異常處理
- 將異常封裝在Task對象中
- 支持統(tǒng)一的異常捕獲和處理機制
線程池使用隊列機制來管理任務:
- 全局隊列(Global Queue):接收所有新提交的任務
- 本地隊列(Local Queue):每個工作線程都有自己的本地隊列,用于存放從全局隊列中獲取的任務
任務處理流程
- 任務提交:新任務進入全局隊列
- 工作線程分配:空閑線程從全局隊列獲取任務執(zhí)行
- 工作竊取(Work Stealing):當線程本地隊列為空時,可以從其他線程的本地隊列"竊取"任務
- 線程管理:線程池根據(jù)任務數(shù)量動態(tài)調(diào)整工作線程數(shù)
- 任務調(diào)度:使用先進先出(FIFO)原則處理隊列中的任務
這種設計實現(xiàn)了高效的負載均衡,避免了線程頻繁創(chuàng)建銷毀的開銷,同時通過工作竊取機制提高了CPU利用率。
由此了解,上述代碼的執(zhí)行延遲的原因可能是:
定時器回調(diào)提交任務到線程池 → 任務進入全局隊列 → 線程池分配可用線程執(zhí)行 → 當長時間運行的數(shù)據(jù)庫任務占用所有線程時,新的定時器任務只能在隊列中等待 → 導致執(zhí)行延遲。
然后進行一些驗證工作:
排除定時器的原因:去掉定時器,使用while(true)不停創(chuàng)建Task,結果仍然存在延遲問題。
private void Form1_Load(object sender,EventArgs e)
{
BatchQuerySVData(null);
}
public static async void BatchQuerySVData(object state)
{
while(true)
{
await Task.Run(()=>{
Console.WriteLine(Index++);
Thread.Sleep(200);
});
}
}進一步驗證,改用Thread專用線程處理定時任務
private Thread _timerThread;
private void Form1_Load(object sender, EventArgs e)
{
_timerThread = new Thread(() =>
{
while (true)
{
Console.WriteLine(Index++);
Thread.Sleep(250); // 精確控制間隔
}
})
{ IsBackground = true };
_timerThread.Start();
}
修改后果然沒有延遲問題了。
問題原因總結:
1.雙重排隊:定時器回調(diào)本身就是在線程池處理,內(nèi)部又使用Task.Run創(chuàng)建了另一個線程池任務
2.線程饑餓:長時間運行的數(shù)據(jù)庫任務(10秒)占用線程池線程,導致定時器任務無法及時獲取線程,且存在大量高并發(fā)任務提交影響程序性能和響應能力。
3.線程切換開銷:頻繁創(chuàng)建短生命周期任務引發(fā)線程切換開銷增大,后續(xù)任務排隊等待。
4.隊列堆積:當線程池所有線程都被長時間任務占用時,新的定時器任務只能在隊列中等待。
到此這篇關于C# Task.Run使用問題解決的文章就介紹到這了,更多相關C# Task.Run使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#中string.Compare 比較兩個字符串的字典順序
C#中string.Compare方法用于比較兩個字符串的字典順序,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-07-07
使用C#實現(xiàn)網(wǎng)頁內(nèi)容保存為圖片并生成壓縮包
這篇文章主要為大家詳細介紹了如何使用C#實現(xiàn)網(wǎng)頁內(nèi)容保存為圖片并生成壓縮包,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-02-02
Unity5.6大規(guī)模地形資源創(chuàng)建方法
這篇文章主要為大家詳細介紹了Unity5.6大規(guī)模地形資源創(chuàng)建方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02

