到底该用多少线程?线程数、CPU核心数、本地计算时间、等待时间的关系 线程数 = CPU核心数 * ( 本地计算时间 + 等待时间 ) / 本地计算时间

下面是Task.Factory.StartNew和自己写的TaskHelper.LargeTask.Run对比测试

一、Task.Factory.StartNew 使用 TaskCreationOptions.LongRunning 参数

代码:

  1. private int n = ; //问题规模
  2. private int t = ; //等待时间
  3. private int pageSize = ; //打印分页
  4.  
  5. private void TestTaskStartNew()
  6. {
  7. Task.Factory.StartNew(() =>
  8. {
  9. Stopwatch stopwatch = Stopwatch.StartNew();
  10.  
  11. List<Task> taskList = new List<Task>();
  12. for (int i = ; i <= n; i++)
  13. {
  14. Task task = Task.Factory.StartNew((obj) =>
  15. {
  16. Thread.Sleep(t); //等待时间
  17.  
  18. int index = (int)obj;
  19. if (index % pageSize == )
  20. {
  21. this.TryInvoke2(() =>
  22. {
  23. textBox1.AppendText(index.ToString() + " ");
  24. });
  25. }
  26. }, i, TaskCreationOptions.LongRunning);
  27. taskList.Add(task);
  28. }
  29. Task.WaitAll(taskList.ToArray());
  30.  
  31. this.TryInvoke2(() =>
  32. {
  33. textBox1.AppendText(string.Format("\r\n【Task.Factory.StartNew 问题规模:{0} 等待时间:{1} 耗时:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds));
  34. });
  35. });
  36. }
  37.  
  38. private void TestTaskHelper()
  39. {
  40. Task.Factory.StartNew(() =>
  41. {
  42. Stopwatch stopwatch = Stopwatch.StartNew();
  43.  
  44. List<Task> taskList = new List<Task>();
  45. for (int i = ; i <= n; i++)
  46. {
  47. Task task = TaskHelper.LargeTask.Run((obj) =>
  48. {
  49. Thread.Sleep(t); //等待时间
  50.  
  51. int index = (int)obj;
  52. if (index % pageSize == )
  53. {
  54. this.TryInvoke2(() =>
  55. {
  56. textBox1.AppendText(index.ToString() + " ");
  57. });
  58. }
  59. }, i);
  60. taskList.Add(task);
  61. }
  62. Task.WaitAll(taskList.ToArray());
  63.  
  64. this.TryInvoke2(() =>
  65. {
  66. textBox1.AppendText(string.Format("\r\n【TaskHelper.LargeTask.Run {3}线程 问题规模:{0} 等待时间:{1} 耗时:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds, TaskHelper.LargeTask.ThreadCount));
  67. });
  68. });
  69. }

测试结果:

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 参数

代码:

  1. private int n = ; //问题规模
  2. private int t = ; //等待时间
  3. private int pageSize = ; //打印分页
  4.  
  5. private void TestTaskStartNew()
  6. {
  7. Task.Factory.StartNew(() =>
  8. {
  9. Stopwatch stopwatch = Stopwatch.StartNew();
  10.  
  11. List<Task> taskList = new List<Task>();
  12. for (int i = ; i <= n; i++)
  13. {
  14. Task task = Task.Factory.StartNew((obj) =>
  15. {
  16. Thread.Sleep(t); //等待时间
  17.  
  18. int index = (int)obj;
  19. if (index % pageSize == )
  20. {
  21. this.TryInvoke2(() =>
  22. {
  23. textBox1.AppendText(index.ToString() + " ");
  24. });
  25. }
  26. }, i);
  27. taskList.Add(task);
  28. }
  29. Task.WaitAll(taskList.ToArray());
  30.  
  31. this.TryInvoke2(() =>
  32. {
  33. textBox1.AppendText(string.Format("\r\n【Task.Factory.StartNew 问题规模:{0} 等待时间:{1} 耗时:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds));
  34. });
  35. });
  36. }
  37.  
  38. private void TestTaskHelper()
  39. {
  40. Task.Factory.StartNew(() =>
  41. {
  42. Stopwatch stopwatch = Stopwatch.StartNew();
  43.  
  44. List<Task> taskList = new List<Task>();
  45. for (int i = ; i <= n; i++)
  46. {
  47. Task task = TaskHelper.LargeTask.Run((obj) =>
  48. {
  49. Thread.Sleep(t); //等待时间
  50.  
  51. int index = (int)obj;
  52. if (index % pageSize == )
  53. {
  54. this.TryInvoke2(() =>
  55. {
  56. textBox1.AppendText(index.ToString() + " ");
  57. });
  58. }
  59. }, i);
  60. taskList.Add(task);
  61. }
  62. Task.WaitAll(taskList.ToArray());
  63.  
  64. this.TryInvoke2(() =>
  65. {
  66. textBox1.AppendText(string.Format("\r\n【TaskHelper.LargeTask.Run {3}线程 问题规模:{0} 等待时间:{1} 耗时:{2}秒】\r\n", n, t, stopwatch.Elapsed.TotalSeconds, TaskHelper.LargeTask.ThreadCount));
  67. });
  68. });
  69. }

测试结果:

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代码:

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9.  
  10. namespace Utils
  11. {
  12. public class LimitedTaskScheduler : TaskScheduler, IDisposable
  13. {
  14. #region 外部方法
  15. [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
  16. public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
  17. #endregion
  18.  
  19. #region 变量属性事件
  20. private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
  21. List<Thread> _threadList = new List<Thread>();
  22. private int _threadCount = ;
  23. private int _timeOut = Timeout.Infinite;
  24. private Task _tempTask;
  25.  
  26. public int ThreadCount
  27. {
  28. get
  29. {
  30. return _threadCount;
  31. }
  32. }
  33. #endregion
  34.  
  35. #region 构造函数
  36. public LimitedTaskScheduler(int threadCount = )
  37. {
  38. CreateThreads(threadCount);
  39. }
  40. #endregion
  41.  
  42. #region override GetScheduledTasks
  43. protected override IEnumerable<Task> GetScheduledTasks()
  44. {
  45. return _tasks;
  46. }
  47. #endregion
  48.  
  49. #region override TryExecuteTaskInline
  50. protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
  51. {
  52. return false;
  53. }
  54. #endregion
  55.  
  56. #region override QueueTask
  57. protected override void QueueTask(Task task)
  58. {
  59. _tasks.Add(task);
  60. }
  61. #endregion
  62.  
  63. #region 资源释放
  64. /// <summary>
  65. /// 资源释放
  66. /// 如果尚有任务在执行,则会在调用此方法的线程上引发System.Threading.ThreadAbortException,请使用Task.WaitAll等待任务执行完毕后,再调用该方法
  67. /// </summary>
  68. public void Dispose()
  69. {
  70. _timeOut = ;
  71.  
  72. foreach (Thread item in _threadList)
  73. {
  74. item.Abort();
  75. }
  76. _threadList.Clear();
  77.  
  78. GC.Collect();
  79. GC.WaitForPendingFinalizers();
  80. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  81. {
  82. SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -, -);
  83. }
  84. }
  85. #endregion
  86.  
  87. #region 创建线程池
  88. /// <summary>
  89. /// 创建线程池
  90. /// </summary>
  91. private void CreateThreads(int? threadCount = null)
  92. {
  93. if (threadCount != null) _threadCount = threadCount.Value;
  94. _timeOut = Timeout.Infinite;
  95.  
  96. for (int i = ; i < _threadCount; i++)
  97. {
  98. Thread thread = new Thread(new ThreadStart(() =>
  99. {
  100. Task task;
  101. while (_tasks.TryTake(out task, _timeOut))
  102. {
  103. TryExecuteTask(task);
  104. }
  105. }));
  106. thread.IsBackground = true;
  107. thread.Start();
  108. _threadList.Add(thread);
  109. }
  110. }
  111. #endregion
  112.  
  113. #region 全部取消
  114. /// <summary>
  115. /// 全部取消
  116. /// </summary>
  117. public void CancelAll()
  118. {
  119. while (_tasks.TryTake(out _tempTask)) { }
  120. }
  121. #endregion
  122.  
  123. }
  124. }

TaskHelper代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace Utils
  8. {
  9. /// <summary>
  10. /// Task帮助类基类
  11. /// </summary>
  12. public class TaskHelper
  13. {
  14. #region UI任务
  15. private static LimitedTaskScheduler _UITask;
  16. /// <summary>
  17. /// UI任务(4个线程)
  18. /// </summary>
  19. public static LimitedTaskScheduler UITask
  20. {
  21. get
  22. {
  23. if (_UITask == null) _UITask = new LimitedTaskScheduler();
  24. return _UITask;
  25. }
  26. }
  27. #endregion
  28.  
  29. #region 计算任务
  30. private static LimitedTaskScheduler _CalcTask;
  31. /// <summary>
  32. /// 计算任务(8个线程)
  33. /// </summary>
  34. public static LimitedTaskScheduler CalcTask
  35. {
  36. get
  37. {
  38. if (_CalcTask == null) _CalcTask = new LimitedTaskScheduler();
  39. return _CalcTask;
  40. }
  41. }
  42. #endregion
  43.  
  44. #region 网络请求
  45. private static LimitedTaskScheduler _RequestTask;
  46. /// <summary>
  47. /// 网络请求(32个线程)
  48. /// </summary>
  49. public static LimitedTaskScheduler RequestTask
  50. {
  51. get
  52. {
  53. if (_RequestTask == null) _RequestTask = new LimitedTaskScheduler();
  54. return _RequestTask;
  55. }
  56. }
  57. #endregion
  58.  
  59. #region 数据库任务
  60. private static LimitedTaskScheduler _DBTask;
  61. /// <summary>
  62. /// 数据库任务(32个线程)
  63. /// </summary>
  64. public static LimitedTaskScheduler DBTask
  65. {
  66. get
  67. {
  68. if (_DBTask == null) _DBTask = new LimitedTaskScheduler();
  69. return _DBTask;
  70. }
  71. }
  72. #endregion
  73.  
  74. #region IO任务
  75. private static LimitedTaskScheduler _IOTask;
  76. /// <summary>
  77. /// IO任务(8个线程)
  78. /// </summary>
  79. public static LimitedTaskScheduler IOTask
  80. {
  81. get
  82. {
  83. if (_IOTask == null) _IOTask = new LimitedTaskScheduler();
  84. return _IOTask;
  85. }
  86. }
  87. #endregion
  88.  
  89. #region 大线程池任务
  90. private static LimitedTaskScheduler _LargeTask;
  91. /// <summary>
  92. /// 大线程池任务(64个线程)
  93. /// </summary>
  94. public static LimitedTaskScheduler LargeTask
  95. {
  96. get
  97. {
  98. if (_LargeTask == null) _LargeTask = new LimitedTaskScheduler();
  99. return _LargeTask;
  100. }
  101. }
  102. #endregion
  103.  
  104. }
  105. }

Task.Factory.StartNew 测试的更多相关文章

  1. Task.Factory.StartNew的用法

    代码: private void button5_Click(object sender, EventArgs e) { ; Task.Factory.StartNew(() => { Mess ...

  2. Task.Run Vs Task.Factory.StartNew

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  3. Task.Run Vs Task.Factory.StartNew z

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  4. Task.Run与Task.Factory.StartNew的区别

    Task是可能有延迟的工作单元,目的是生成一个结果值,或产生想要的效果.任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者. 在.Net 4中,Task.Factory.Star ...

  5. C# Task.Run 和 Task.Factory.StartNew 区别

    Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制.可以认 ...

  6. Task.Run Vs Task.Factory.StartNew 【收藏】

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  7. c#4.0 Task.Factory.StartNew 用法

    var t1 = Task.Factory.StartNew<string>(() => { return “1111111”; }); //t1.Wait(); t1.Contin ...

  8. task.factory.startnew()

    1.委托: public delegate int Math(int param1,int param2);定义委托类型 Public int Add(int param1,int param2)// ...

  9. 单线程任务 Task.Factory.StartNew 封装

    代码: using log4net; using SunCreate.CombatPlatform.Security; using System; using System.Collections.G ...

随机推荐

  1. 【Luogu P2024&P1892】食物链&团伙(并查集拓展域)

    Luogu P1892 Luogu P2024 这两道一眼看过去很容易发现可以用并查集来做--但是当我们仔细阅读题面后,会发现其实并没有那么简单. 我们知道并查集可以很轻松地维护具有传递性的信息,也就 ...

  2. .NET Core 3.0之深入源码理解HealthCheck(一)

    写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...

  3. Docker部署Mysql集群

    单节点数据库的弊病 大型互联网程序用户群体庞大,所以架构必须要特殊设计 单节点的数据库无法满足性能上的要求 单节点的数据库没有冗余设计,无法满足高可用 单节点MySQL的性能瓶领颈 2016年春节微信 ...

  4. C语言博客作业11

    一.本周教学内容&目标 第5章 函数 要求学生掌握各种类型函数的定义.调用和申明,熟悉变量的作用域.生存周期和存储类型. 二.本周作业头 这个作业属于那个课程 C语言程序设计II 这个作业要求 ...

  5. 利用scatter()绘制颜色映射的二次方曲线

    程序如下: import matplotlib.pyplot as plt x_value = list(range(1, 1001)) y_value = [x**2 for x in x_valu ...

  6. js消除小游戏(极简版)

    js小游戏极简版 (1) 基础布局 <div class = "box"> <p></p> <div class="div&qu ...

  7. js人民币转大写

    <input type="text" oninput="OnInput (event)" value="1234567"> &l ...

  8. SpringBoot系列之集成jsp模板引擎

    目录 1.模板引擎简介 2.环境准备 4.源码原理简介 SpringBoot系列之集成jsp模板引擎 @ 1.模板引擎简介 引用百度百科的模板引擎解释: 模板引擎(这里特指用于Web开发的模板引擎)是 ...

  9. uni-app之网络请求

    uni-app之网络请求 一,介绍 uni.request(OBJECT),发起网络请求,以下主要是一些特殊的参数说明,详细的可查看uni-app官网. 1,method的有效值必须是大写,默认GET ...

  10. ModelArts微认证零售客户分群知识点总结

    \ 作者:华为云MVP郑永祥