Task.Factory.StartNew 測試

来源:https://www.cnblogs.com/s0611163/archive/2019/12/14/12041295.html
-Advertisement-
Play Games

到底該用多少線程?線程數、CPU核心數、本地計算時間、等待時間的關係 線程數 = CPU核心數 * ( 本地計算時間 + 等待時間 ) / 本地計算時間 下麵是Task.Factory.StartNew和自己寫的TaskHelper.LargeTask.Run對比測試 一、Task.Factory. ...


到底該用多少線程?線程數、CPU核心數、本地計算時間、等待時間的關係 線程數 = CPU核心數 * ( 本地計算時間 + 等待時間 ) / 本地計算時間

下麵是Task.Factory.StartNew和自己寫的TaskHelper.LargeTask.Run對比測試

 

一、Task.Factory.StartNew 使用 TaskCreationOptions.LongRunning 參數

代碼:

private int n = 50000; //問題規模
private int t = 25; //等待時間
private int pageSize = 1000; //列印分頁

private void TestTaskStartNew()
{
    Task.Factory.StartNew(() =>
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        List<Task> taskList = new List<Task>();
        for (int i = 0; i <= n; i++)
        {
            Task task = Task.Factory.StartNew((obj) =>
            {
                Thread.Sleep(t); //等待時間

                int index = (int)obj;
                if (index % pageSize == 0)
                {
                    this.TryInvoke2(() =>
                    {
                        textBox1.AppendText(index.ToString() + "  ");
                    });
                }
            }, i, TaskCreationOptions.LongRunning);
            taskList.Add(task);
        }
        Task.WaitAll(taskList.ToArray());

        this.TryInvoke2(() =>
        {
            textBox1.AppendText(string.Format("\r\n【Task.Factory.StartNew 問題規模:{0} 等待時間:{1} 耗時:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds));
        });
    });
}

private void TestTaskHelper()
{
    Task.Factory.StartNew(() =>
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        List<Task> taskList = new List<Task>();
        for (int i = 0; i <= n; i++)
        {
            Task task = TaskHelper.LargeTask.Run((obj) =>
            {
                Thread.Sleep(t); //等待時間

                int index = (int)obj;
                if (index % pageSize == 0)
                {
                    this.TryInvoke2(() =>
                    {
                        textBox1.AppendText(index.ToString() + "  ");
                    });
                }
            }, i);
            taskList.Add(task);
        }
        Task.WaitAll(taskList.ToArray());

        this.TryInvoke2(() =>
        {
            textBox1.AppendText(string.Format("\r\n【TaskHelper.LargeTask.Run {3}線程 問題規模:{0} 等待時間:{1} 耗時:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds, TaskHelper.LargeTask.ThreadCount));
        });
    });
}
View Code

測試結果:

0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000 16000 17000 18000 19000 20000 21000 22000 23000 24000 25000 26000 27000 28000 29000 30000 31000 32000 33000 34000 35000 36000 37000 38000 39000 40000 41000 42000 43000 44000 45000 46000 47000 48000 49000 50000
【TaskHelper.LargeTask.Run 128線程 問題規模:50000 等待時間:25 耗時:10.5975181秒】
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000 16000 17000 18000 19000 20000 21000 22000 23000 24000 25000 26000 27000 28000 29000 30000 31000 32000 33000 34000 35000 36000 37000 38000 39000 40000 41000 42000 43000 44000 45000 46000 47000 48000 49000 50000
【Task.Factory.StartNew 問題規模:50000 等待時間:25 耗時:8.2380754秒】
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000 16000 17000 18000 19000 20000 21000 22000 23000 24000 25000 26000 27000 28000 29000 30000 31000 32000 33000 34000 35000 36000 37000 38000 39000 40000 41000 42000 43000 44000 45000 46000 47000 48000 49000 50000
【TaskHelper.LargeTask.Run 128線程 問題規模:50000 等待時間:25 耗時:10.4376939秒】
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000 16000 17000 18000 19000 20000 21000 22000 23000 24000 25000 26000 27000 28000 29000 30000 31000 32000 33000 34000 35000 36000 37000 38000 39000 40000 41000 42000 43000 44000 45000 46000 47000 48000 49000 50000
【Task.Factory.StartNew 問題規模:50000 等待時間:25 耗時:9.2322552秒】

測試結果說明:

我的電腦的CPU是i5-8265U,4核8線程
根據等待時間設置合適的線程數對TaskHelper.LargeTask.Run有利
使用TaskHelper.LargeTask.Run運行時的CPU占用在5%以下,創建128個線程的瞬間CPU占用達到30%,使用Task.Factory.StartNew運行時的CPU占用接近100%
資源釋放情況:Task.Factory.StartNew使用TaskCreationOptions.LongRunning參數運行完成後線程數立即釋放,句柄數未立即釋放,而TaskHelper.LargeTask.Run提供了手動釋放的方法可以立即釋放線程數和句柄數,但需要手動調用才能釋放

 

 二、Task.Factory.StartNew 不使用 TaskCreationOptions.LongRunning 參數

代碼:

private int n = 2000; //問題規模
private int t = 100; //等待時間
private int pageSize = 100; //列印分頁

private void TestTaskStartNew()
{
    Task.Factory.StartNew(() =>
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        List<Task> taskList = new List<Task>();
        for (int i = 0; i <= n; i++)
        {
            Task task = Task.Factory.StartNew((obj) =>
            {
                Thread.Sleep(t); //等待時間

                int index = (int)obj;
                if (index % pageSize == 0)
                {
                    this.TryInvoke2(() =>
                    {
                        textBox1.AppendText(index.ToString() + "  ");
                    });
                }
            }, i);
            taskList.Add(task);
        }
        Task.WaitAll(taskList.ToArray());

        this.TryInvoke2(() =>
        {
            textBox1.AppendText(string.Format("\r\n【Task.Factory.StartNew 問題規模:{0} 等待時間:{1} 耗時:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds));
        });
    });
}

private void TestTaskHelper()
{
    Task.Factory.StartNew(() =>
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        List<Task> taskList = new List<Task>();
        for (int i = 0; i <= n; i++)
        {
            Task task = TaskHelper.LargeTask.Run((obj) =>
            {
                Thread.Sleep(t); //等待時間

                int index = (int)obj;
                if (index % pageSize == 0)
                {
                    this.TryInvoke2(() =>
                    {
                        textBox1.AppendText(index.ToString() + "  ");
                    });
                }
            }, i);
            taskList.Add(task);
        }
        Task.WaitAll(taskList.ToArray());

        this.TryInvoke2(() =>
        {
            textBox1.AppendText(string.Format("\r\n【TaskHelper.LargeTask.Run {3}線程 問題規模:{0} 等待時間:{1} 耗時:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds, TaskHelper.LargeTask.ThreadCount));
        });
    });
}
View Code

測試結果:

0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000
【TaskHelper.LargeTask.Run 96線程 問題規模:2000 等待時間:100 耗時:2.1529565秒】
0 2000 100 200 300 400 500 600 700 800 900 1900 1000 1100 1200 1300 1400 1500 1600 1700 1800
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:17.309869秒】
0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000
【TaskHelper.LargeTask.Run 96線程 問題規模:2000 等待時間:100 耗時:2.143763秒】
0 2000 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:8.8674353秒】
0 2000 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:6.5490833秒】
0 2000 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:5.1381533秒】
0 2000 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:4.434294秒】
0 2000 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:4.329009秒】
2000 0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:3.6231239秒】
2000 0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900
【Task.Factory.StartNew 問題規模:2000 等待時間:100 耗時:3.6303149秒】

測試結論:

Task.Factory.StartNew在不使用TaskCreationOptions.LongRunning參數時,運行大量耗時任務,線程數增加緩慢,導致需要花費很長時間,如果線程池耗盡,或者線程池未耗盡但有大量耗時任務時,其它任務調用Task.Factory.StartNew會有延遲

我想了一天,多任務還是不要共用線程池比較好,一個任務一個線程池,互不幹擾,TaskHelper.LargeTask.Run就是按這個思路寫的,不知道可有問題

 

附:

LimitedTaskScheduler代碼:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    public class LimitedTaskScheduler : TaskScheduler, IDisposable
    {
        #region 外部方法
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
        public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
        #endregion

        #region 變數屬性事件
        private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
        List<Thread> _threadList = new List<Thread>();
        private int _threadCount = 0;
        private int _timeOut = Timeout.Infinite;
        private Task _tempTask;

        public int ThreadCount
        {
            get
            {
                return _threadCount;
            }
        }
        #endregion

        #region 構造函數
        public LimitedTaskScheduler(int threadCount = 10)
        {
            CreateThreads(threadCount);
        }
        #endregion

        #region override GetScheduledTasks
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return _tasks;
        }
        #endregion

        #region override TryExecuteTaskInline
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return false;
        }
        #endregion

        #region override QueueTask
        protected override void QueueTask(Task task)
        {
            _tasks.Add(task);
        }
        #endregion

        #region 資源釋放
        /// <summary>
        /// 資源釋放
        /// 如果尚有任務在執行,則會在調用此方法的線程上引發System.Threading.ThreadAbortException,請使用Task.WaitAll等待任務執行完畢後,再調用該方法
        /// </summary>
        public void Dispose()
        {
            _timeOut = 100;

            foreach (Thread item in _threadList)
            {
                item.Abort();
            }
            _threadList.Clear();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }
        #endregion

        #region 創建線程池
        /// <summary>
        /// 創建線程池
        /// </summary>
        private void CreateThreads(int? threadCount = null)
        {
            if (threadCount != null) _threadCount = threadCount.Value;
            _timeOut = Timeout.Infinite;

            for (int i = 0; i < _threadCount; i++)
            {
                Thread thread = new Thread(new ThreadStart(() =>
                {
                    Task task;
                    while (_tasks.TryTake(out task, _timeOut))
                    {
                        TryExecuteTask(task);
                    }
                }));
                thread.IsBackground = true;
                thread.Start();
                _threadList.Add(thread);
            }
        }
        #endregion

        #region 全部取消
        /// <summary>
        /// 全部取消
        /// </summary>
        public void CancelAll()
        {
            while (_tasks.TryTake(out _tempTask)) { }
        }
        #endregion

    }
}
View Code

TaskHelper代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// Task幫助類基類
    /// </summary>
    public class TaskHelper
    {
        #region UI任務
        private static LimitedTaskScheduler _UITask;
        /// <summary>
        /// UI任務(4個線程)
        /// </summary>
        public static LimitedTaskScheduler UITask
        {
            get
            {
                if (_UITask == null) _UITask = new LimitedTaskScheduler(4);
                return _UITask;
            }
        }
        #endregion

        #region 計算任務
        private static LimitedTaskScheduler _CalcTask;
        /// <summary>
        /// 計算任務(8個線程)
        /// </summary>
        public static LimitedTaskScheduler CalcTask
        {
            get
            {
                if (_CalcTask == null) _CalcTask = new LimitedTaskScheduler(8);
                return _CalcTask;
            }
        }
        #endregion

        #region 網路請求
        private static LimitedTaskScheduler _RequestTask;
        /// <summary>
        /// 網路請求(32個線程)
        /// </summary>
        public static LimitedTaskScheduler RequestTask
        {
            get
            {
                if (_RequestTask == null) _RequestTask = new LimitedTaskScheduler(32);
                return _RequestTask;
            }
        }
        #endregion

        #region 資料庫任務
        private static LimitedTaskScheduler _DBTask;
        /// <summary>
        /// 資料庫任務(32個線程)
        /// </summary>
        public static LimitedTaskScheduler DBTask
        {
            get
            {
                if (_DBTask == null) _DBTask = new LimitedTaskScheduler(32);
                return _DBTask;
            }
        }
        #endregion

        #region IO任務
        private static LimitedTaskScheduler _IOTask;
        /// <summary>
        /// IO任務(8個線程)
        /// </summary>
        public static LimitedTaskScheduler IOTask
        {
            get
            {
                if (_IOTask == null) _IOTask = new LimitedTaskScheduler(8);
                return _IOTask;
            }
        }
        #endregion

        #region 大線程池任務
        private static LimitedTaskScheduler _LargeTask;
        /// <summary>
        /// 大線程池任務(64個線程)
        /// </summary>
        public static LimitedTaskScheduler LargeTask
        {
            get
            {
                if (_LargeTask == null) _LargeTask = new LimitedTaskScheduler(128);
                return _LargeTask;
            }
        }
        #endregion

    }
}
View Code

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今年下半年,我正式系統地學習Java(之前學習C++)。最近把學習Java所用到的書籍整理了一下,分享出來,希望對正在學習或準備學習Java的人有一定的幫助。 關於Java的學習路線,和IDE工具IntelliJ IDEA的安裝使用可以看我之前的文章。當然,我也在不斷更新自己的學習總結。也可以關註我 ...
  • 瞭解如何使用Spring Boot和AspectJ實現方法跟蹤基礎結構!最近在優銳課學習收穫頗多,記錄下來大家一起進步! 在我們的應用程式中,獲取方法的堆棧跟蹤信息可能會節省很多時間。具有輸入輸出參數值和方法所花費的時間可以使查找問題變得更加容易。在本文中,我們將研究如何使用Spring Boot, ...
  • 1.如何定時任務 1.1 開啟定時任務 @EnableScheduling //開啟定時任務 @SpringBootApplication public class ManagerApplication { private static Logger logger = LoggerFactory.g ...
  • 做web開發的時候,我們往往會有很多靜態資源,如html、圖片、css等。那如何向前端返回靜態資源呢?以前做過web開發的同學應該知道,我們以前創建的web工程下麵會有一個webapp的目錄,我們只要把靜態資源放在該目錄下就可以直接訪問。但是,基於Spring boot的工程並沒有這個目錄,那我們應 ...
  • 解決方法: 1. javac -encoding UTF-8 .\Test_Sql.java //加上參數指定utf-8 2.改變文件編碼 使用 ANSI編碼。 ...
  • 人生,抬頭就不能看到地面,低頭就不能仰望天空。記住還記住的,忘記該忘記的,改變能改變的,接受不能改變的。何必無理智的苛求?該是你的,躲著躲不過,不是你的,求也求不來。 ...
  • 本文是Spring Cloud專欄的第七篇文章,瞭解前六篇文章內容有助於更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用組件介紹概覽 Spring Cloud第二篇 | 使用並認識Eureka註冊中心 Spring Cloud第三篇 | 搭建高可用Eurek ...
  • 我的使用的是Visual Studio 2019 原因 學習完一部分東西後,我發現了一個更牛b的東西ABP框架。 所以開始學習ABP框架 https://github.com/aspnetboilerplate/aspnetboilerplate 問題來了, 1.怎麼學習? 去官網直接生成項目【ht ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...