网站首页 > 技术文章 正文
译文链接: https://www.infoworld.com/article/3063560/how-to-build-your-own-task-scheduler-in-csharp.html
TPL(任务并行库)是 .NET Framework 最新版本中最有趣的新功能之一,在.NET Framework 4.0 中被引入。要使用TPL,您需要引用 System.Threading.Tasks 命名空间。
什么是 task schedulers?为什么需要它们?
如果我想问,Task 是如何被调度执行的?其实有一个组件叫做 task scheduler,它专门负责调度 task。从本质上来说,这是一个底层对象的抽象层,它可以将您的 task 通过排队调度到线程上。
.NET Framework 为您提供了两个 task schedulers。包括基于默认的 task scheduler 用于调度在 Thread Pool 之上,另一个 task scheduler 能够调度在 指定对象的同步上下文上。请注意,TPL的默认 task scheduler 利用了.NET Framework 线程池。该线程池的表示类 ThreadPool 是在 System.Threading.Tasks命名空间下。
尽管默认的 task scheduler 在大多数情况下已足够,但有时候你也许也想构建自定义的 task scheduler 去完成个性化的功能,比如那些默认的 task scheduler 没有提供的。此类功能可能包括 FIFO执行,并发度等。
在C#中扩展 TaskScheduler
要构建自定义的 task scheduler,您需要创建一个类并继承 System.Threading.Tasks.TaskScheduler 。因此,要构建自定义的 task scheduler,您需要扩展 TaskScheduler抽象类并重写以下方法。
- QueueTask 返回void并接受Task对象作为参数,当一个 task 需要调度的时候就要调用这个方法
- GetScheduledTasks 返回所有已被调用的 task list, (准确的说是IEnumerable)
- TryExecuteTaskInline 用于内联方式(即在当前线程上)执行task。在这种情况下,无需排队即可执行 task
下面的代码段展示了如何继承 TaskScheduler 类来实现我们自定义的 scheduler。
public class CustomTaskScheduler : TaskScheduler, IDisposable
{
}
正如在本文前面所讨论的,您需要在 CustomTaskScheduler 中重写下面3个方法: GetScheduledTasks,QueueTask和TryExecuteTaskInline。
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
protected override IEnumerable<Task> GetScheduledTasks()
{
//TODO
}
protected override void QueueTask(Task task)
{
//TODO
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
//TODO
}
public void Dispose()
{
//TODO
}
}
使用 BlockingCollection 来存储 task对象集合
现在我们开始实现自定义的 task scheduler。以下代码段显示了如何利用 BlockingCollection 存储 task对象的集合。
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>();
private readonly Thread mainThread = null;
public CustomTaskScheduler()
{
mainThread = new Thread(new ThreadStart(Execute));
if (!mainThread.IsAlive)
{
mainThread.Start();
}
}
private void Execute()
{
foreach (var task in tasksCollection.GetConsumingEnumerable())
{
TryExecuteTask(task);
}
}
//Other methods
}
可以着重看看 CustomTaskScheduler 类的构造函数,一个 thread 是如何被创建并且如何开始调度 Execute 方法的。
实现 GetScheduledTasks,QueueTask 和 TryExecuteTaskInline
接下来,我们需要在 CustomTaskScheduler 中实现三个自定义方法。这三种方法包括 GetScheduledTasks,QueueTask 和 TryExecuteTaskInline。
GetScheduledTasks方法返回 IEnumerable 类型的 task 集合,这样你就可以迭代这个 collection,就像上面 Execute 方法一样。QueueTask方法接受Task对象作为方法参数,然后将其存储在 task collection 中,TryExecuteTaskInline方法暂不实现, 我准备将它留给读者来实现。
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasksCollection.ToArray();
}
protected override void QueueTask(Task task)
{
if (task != null)
tasksCollection.Add(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
完整的 CustomTaskScheduler 代码如下
下面是最终版本的 CustomTaskScheduler 代码清单:
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>();
private readonly Thread mainThread = null;
public CustomTaskScheduler()
{
mainThread = new Thread(new ThreadStart(Execute));
if (!mainThread.IsAlive)
{
mainThread.Start();
}
}
private void Execute()
{
foreach (var task in tasksCollection.GetConsumingEnumerable())
{
TryExecuteTask(task);
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasksCollection.ToArray();
}
protected override void QueueTask(Task task)
{
if (task != null)
tasksCollection.Add(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
private void Dispose(bool disposing)
{
if (!disposing) return;
tasksCollection.CompleteAdding();
tasksCollection.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
要使用我们刚刚实现的 CustomTaskScheduler ,您可以使用以下代码段:
CustomTaskScheduler taskScheduler = new CustomTaskScheduler();
Task.Factory.StartNew(() => SomeMethod(), CancellationToken.None, TaskCreationOptions.None, taskScheduler);
更多高质量干货:参见我的 GitHub: dotnetfly**
猜你喜欢
- 2024-10-04 C# 类和对象(c#简述类和对象的关系)
- 2024-10-04 C# Win Form 窗体间传递参数的几种方法总结
- 2024-10-04 面试官常问:关于Redis的分布式锁设计,你是如何实现的?
- 2024-10-04 互联网面试——C# 面试题(c#面试题2020)
- 2024-10-04 C#-实例与静态成员,访问修饰符,命名空间,启动项目 080
- 2024-10-04 C#在类型的外部不能声明全局变量,与C,C++对比,示例说明
- 10-02基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- 10-02Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- 10-02AWD-LSTM语言模型是如何实现的_lstm语言模型
- 10-02NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 10-02使用ONNX和Torchscript加快推理速度的测试
- 10-02tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- 10-02Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- 10-02Gateway_gateways
- 最近发表
-
- 基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- AWD-LSTM语言模型是如何实现的_lstm语言模型
- NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 使用ONNX和Torchscript加快推理速度的测试
- tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- Gateway_gateways
- Coze开源本地部署教程_开源canopen
- 扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)