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

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

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

代码:

private int n = ; //问题规模
private int t = ; //等待时间
private int pageSize = ; //打印分页 private void TestTaskStartNew()
{
Task.Factory.StartNew(() =>
{
Stopwatch stopwatch = Stopwatch.StartNew(); List<Task> taskList = new List<Task>();
for (int i = ; i <= n; i++)
{
Task task = Task.Factory.StartNew((obj) =>
{
Thread.Sleep(t); //等待时间 int index = (int)obj;
if (index % pageSize == )
{
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 = ; i <= n; i++)
{
Task task = TaskHelper.LargeTask.Run((obj) =>
{
Thread.Sleep(t); //等待时间 int index = (int)obj;
if (index % pageSize == )
{
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));
});
});
}

测试结果:

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 = ; //问题规模
private int t = ; //等待时间
private int pageSize = ; //打印分页 private void TestTaskStartNew()
{
Task.Factory.StartNew(() =>
{
Stopwatch stopwatch = Stopwatch.StartNew(); List<Task> taskList = new List<Task>();
for (int i = ; i <= n; i++)
{
Task task = Task.Factory.StartNew((obj) =>
{
Thread.Sleep(t); //等待时间 int index = (int)obj;
if (index % pageSize == )
{
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 = ; i <= n; i++)
{
Task task = TaskHelper.LargeTask.Run((obj) =>
{
Thread.Sleep(t); //等待时间 int index = (int)obj;
if (index % pageSize == )
{
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));
});
});
}

测试结果:

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 = ;
private int _timeOut = Timeout.Infinite;
private Task _tempTask; public int ThreadCount
{
get
{
return _threadCount;
}
}
#endregion #region 构造函数
public LimitedTaskScheduler(int threadCount = )
{
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 = ; 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, -, -);
}
}
#endregion #region 创建线程池
/// <summary>
/// 创建线程池
/// </summary>
private void CreateThreads(int? threadCount = null)
{
if (threadCount != null) _threadCount = threadCount.Value;
_timeOut = Timeout.Infinite; for (int i = ; 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 }
}

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();
return _UITask;
}
}
#endregion #region 计算任务
private static LimitedTaskScheduler _CalcTask;
/// <summary>
/// 计算任务(8个线程)
/// </summary>
public static LimitedTaskScheduler CalcTask
{
get
{
if (_CalcTask == null) _CalcTask = new LimitedTaskScheduler();
return _CalcTask;
}
}
#endregion #region 网络请求
private static LimitedTaskScheduler _RequestTask;
/// <summary>
/// 网络请求(32个线程)
/// </summary>
public static LimitedTaskScheduler RequestTask
{
get
{
if (_RequestTask == null) _RequestTask = new LimitedTaskScheduler();
return _RequestTask;
}
}
#endregion #region 数据库任务
private static LimitedTaskScheduler _DBTask;
/// <summary>
/// 数据库任务(32个线程)
/// </summary>
public static LimitedTaskScheduler DBTask
{
get
{
if (_DBTask == null) _DBTask = new LimitedTaskScheduler();
return _DBTask;
}
}
#endregion #region IO任务
private static LimitedTaskScheduler _IOTask;
/// <summary>
/// IO任务(8个线程)
/// </summary>
public static LimitedTaskScheduler IOTask
{
get
{
if (_IOTask == null) _IOTask = new LimitedTaskScheduler();
return _IOTask;
}
}
#endregion #region 大线程池任务
private static LimitedTaskScheduler _LargeTask;
/// <summary>
/// 大线程池任务(64个线程)
/// </summary>
public static LimitedTaskScheduler LargeTask
{
get
{
if (_LargeTask == null) _LargeTask = new LimitedTaskScheduler();
return _LargeTask;
}
}
#endregion }
}

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. python3 之 判断字符串是否只为数字(isdigit()方法、isnumeric()方法)

    Isdigit()方法 - 检测字符串是否只由数字组成 语法:   str.isdigit() 参数: 无 返回值: 如果字符串只包含数字,则返回True,否则返回False. 实例: 以下实例展示了 ...

  2. Java类的定义与类的实例化

    目录 Java类的定义与类的实例化 类的定义 定义一个简单的类 定义一个成员变量 定义一个方法 定义一个构造器 类的实例化 创建对象及使用对象: 创建对象的过程在内存中的表现 Java类的定义与类的实 ...

  3. Java基础语法(二)

    目录 一.强类型语言 二.数据类型分类 1.基本数据类型 整数类型 字符类型 浮点类型 布尔类型 2.引用数据类型 三.基本类型转换 自动类型转换 强制类型转换 四.表达式类型的自动提升 承接上篇,谈 ...

  4. Win10 SQLServer 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序

    环境:Win10+SQLServer2014 场景:在SQLServer导入Excel时,选择Excel2007格式,提示:未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供 ...

  5. 2、Docker 基础安装和基础使用 一

    基础环境 本次环境使用Centos 7.x版本系统,最小化安装,系统基础优化配置请查看 Centos 7.x 系统基础优化 安装 使用命令:yum install docker-io -y [root ...

  6. 转:SQL SERVER 2014 安装图解(含 SQL SERVER 2014 安装程序共享)

    开篇介绍 2015年1月1日,新的一年开始之际,本来应该好好做点有意义的事情来跨个年的.结果,老习惯 - 睡觉之前一定要折腾一下电脑,说干就干,给新到的 DELL 电脑装虚机,下载 SQL SERVE ...

  7. 解决 Docker Hadoop ssh "Connection to * closed".问题

    Docker 最近很火, 可以快速轻量级地虚拟出多个node,所以打算在Docker中跑Hadoop伪分布式应用. 其实要做出个简单的版本倒是不难,主要在 建立ssh无密码登录本机时,出现刚登录上去, ...

  8. Link Binary With Libraries中添加的时候 也找不到libz.dylib 库

    接到一个项目4个静态库找不到 在 Link Binary With Libraries中添加的时候 也找不到libz.dylib  郁闷了 原来是ios9后 原来的dylib后缀名的库全部修改tbd ...

  9. ARTS-S k8s配制文件demo

    apiVersion: extensions/v1beta1 kind: Deployment metadata: name: go-demo-hostname spec: replicas: 2 t ...

  10. 【算法】273-每周一练 之 数据结构与算法(Tree)

    这是第六周的练习题,最近加班比较多. 下面是之前分享的链接: [算法]200-每周一练 之 数据结构与算法(Stack) [算法]213-每周一练 之 数据结构与算法(LinkedList) [算法] ...