到底該用多少執行緒?執行緒數、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
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/91564.html
標籤:C#
上一篇:【后端C#】后臺通過http post 呼叫 webservice 的方法
下一篇:C# 中的基本數值型別
