c# TaskScheduler

这里记录下 TaskScheduler 的简单用法。

使用场景:

使用 Task 的时候,大家知道用 TaskFactory.StartNew 可以用来创建一个 Task 。这里如果创建了 3 个,那么这3个 Task 就各自放飞直接运行了。

csharp 复制代码
class Program
{
	private static TaskFactory _taskFactory;
	static void Main(string[] args)
	{
		_taskFactory = new TaskFactory();
 
		_taskFactory.StartNew(Func1);
		_taskFactory.StartNew(Func2);
		_taskFactory.StartNew(Func3);
 
		Console.ReadLine();
	}
 
	static void Func1()
	{
		Thread.Sleep(3000);
		Console.WriteLine("Func1");
	}
	static void Func2()
	{
		Thread.Sleep(2000);
		Console.WriteLine("Func2");
	}
	static void Func3()
	{
		Thread.Sleep(1000);
		Console.WriteLine("Func3");
	}
}

结果:

Func3

Func2

Func1

一般情况下没什么大问题,但如果这3个中的每个Task都非常耗CPU或者内存,而计算机又需要预留资源去干别的事情,这就要让3个Task不能同时执行。又或者确实要让某些Task先做,有些后做。这就需要我们自己能够决定Task执行顺序。

怎样达到这样的效果?

答案就是:TaskScheduler,它可以让已经创建好的 Task 去按照特殊的顺序来执行。

就拿上面的场景来举例:

为了节约系统资源,我要让这3个Task单独执行,有一个在执行,其它俩就不能执行。最简单的就是串行执行,这样只需要写一个类继承TaskScheduler:

csharp 复制代码
public class MyTaskScheduler : TaskScheduler, IDisposable
{
	private static readonly object _mutex = new object();
	public readonly List<Task> _currentTasks = new List<Task>();
	private readonly ManualResetEvent[] _schedulerEvents = new ManualResetEvent[2];
	public override int MaximumConcurrencyLevel => 1;
 
	public MyTaskScheduler()
	{
		_schedulerEvents[0] = new ManualResetEvent(false);
		_schedulerEvents[1] = new ManualResetEvent(false);
 
		var executionThread = new Thread(ExecutionThread) { Name = "MyThread" };
		executionThread.SetApartmentState(ApartmentState.MTA);
		executionThread.IsBackground = true;
		executionThread.Priority = ThreadPriority.Normal;
		executionThread.Start(null);
	}
 
	public void Dispose()
	{
		_schedulerEvents[1].Set();
	}
 
	private void ExecutionThread(object args)
	{
		try
		{
 
			while (true)
			{
				if (!WaitIfEmpty())
				{
					break;
				}
 
				Task task;
				lock (_mutex)
				{
					task = _currentTasks[0];
				}
 
				try
				{
					TryExecuteTask(task);
				}
				catch (Exception)
				{
					throw;
				}
				finally
				{
					TryDequeue(task);
				}
			}
		}
		finally
		{
			//todo
		}
	}
	private bool WaitIfEmpty()
	{
		lock (_mutex)
		{
			if (_currentTasks.Count == 0)
			{
				//pause task
				_schedulerEvents[0].Reset();
			}
		}
 
		//wait any signal.
		int id = WaitHandle.WaitAny(_schedulerEvents);
 
		// id is item index of _schedulerEvents
		return id == 0;
	}
 
	protected override IEnumerable<Task>? GetScheduledTasks()
	{
		lock (_mutex)
		{
			return _currentTasks.ToArray();
		}
	}
	protected override void QueueTask(Task task)
	{
		lock (_mutex)
		{
			_currentTasks.Add(task);
 
			if (_currentTasks.Count > 0)
			{
				// Start task
				_schedulerEvents[0].Set();
			}
		}
	}
	protected sealed override bool TryDequeue(Task task)
	{
		lock (_mutex)
		{
			var res = _currentTasks.Remove(task);
			return res;
		}
	}
	protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
	{
		return false;
	}
}

运行结果:

Func1

Func2

Func3

在MyTaskScheduler 中,我建了一个线程 executionThread,线程方法是一个while循环,一直在监听有没有新的Task过来,有多个Task过来,就按Task创建顺序执行,没有的话 while 就暂停在 waitany 处。

这样的场景,配合 ManualResetEvent 和Task的CancellationTokenSource 非常适合做 带有暂停和取消功能的任务列表。

相关推荐
椒颜皮皮虾྅4 小时前
OpenVINO C# API 3.3.1:支持OpenVINO 2026.3及GenAI增强
人工智能·c#·openvino
半亩码田7 小时前
C#转Python第3.1篇:Python 的 class 没有访问修饰符?面向对象的另一条路
开发语言·python·c#
唐青枫13 小时前
别再手写 args[0]:C#.NET CommandLineParser 命令行工具实战详解
c#·.net
LccKyI14 小时前
C#学习day06(方法/函数及其关键字,附思维导图)
经验分享·学习·c#
.Peter15 小时前
.NET/WPF 程序在部分 Windows 电脑无法保存用户环境变量:使用 DPAPI 实现安全兼容
windows·信息安全·c#·.net·wpf·环境变量·dpapi
农村小镇哥16 小时前
怎么把HTM格式转化成WORD
ui·c#·word
农村小镇哥16 小时前
C#读取CSV文件的方法
服务器·数据库·c#
FuckPatience16 小时前
C# 报错参数无效
c#
北域码匠1 天前
PID 控制算法完整详解(C# 原生实现,无第三方库)
c#·pid控制·工控开发·闭环控制·自动控制算法·数字pid·增量式pid
离陌在学C#1 天前
C# 重载与重写:深入理解面向对象编程的核心概念
java·c#