131 lines
5.0 KiB
C#
131 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Lskj.Util
|
|
{
|
|
public class TaskSchedulerUtil : TaskScheduler
|
|
{
|
|
/// <summary>
|
|
/// WinForms 会在后台线程第一次构造控件时自动安装
|
|
/// WindowsFormsSynchronizationContext。预加载任务只能执行数据准备,
|
|
/// 不应把线程池线程注册成 WinForms UI 线程;否则 DevExpress manager
|
|
/// 会绑定到已经没有消息循环的线程,导致模块打开/关闭后的残留和卡死。
|
|
/// 这里只在线程池任务执行期间使用一个非 WinForms 的同步上下文
|
|
/// 作为占位,不改变调度器的并发度,也不触碰真正的 UI 线程上下文。
|
|
/// 不能直接使用 SynchronizationContext 基类实例:WinForms 的
|
|
/// InstallIfNeeded 会把“基类实例”视为未安装状态并再次替换为
|
|
/// WindowsFormsSynchronizationContext;必须使用独立派生类型。
|
|
/// </summary>
|
|
private void ExecuteTaskWithoutWinFormsContext(Task task)
|
|
{
|
|
SynchronizationContext previousContext =
|
|
SynchronizationContext.Current;
|
|
bool replaceContext = previousContext == null ||
|
|
previousContext is WindowsFormsSynchronizationContext;
|
|
|
|
if (replaceContext)
|
|
{
|
|
SynchronizationContext.SetSynchronizationContext(
|
|
new BackgroundSynchronizationContext());
|
|
}
|
|
|
|
try
|
|
{
|
|
TryExecuteTask(task);
|
|
}
|
|
finally
|
|
{
|
|
if (replaceContext)
|
|
{
|
|
SynchronizationContext.SetSynchronizationContext(
|
|
previousContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 标记后台线程已明确配置同步上下文,阻止 WinForms 按“空/基类上下文”
|
|
/// 自动安装 WindowsFormsSynchronizationContext。继承基类的默认 Post
|
|
/// 实现仍将延续任务投递到线程池,不改变普通后台任务语义。
|
|
/// </summary>
|
|
private sealed class BackgroundSynchronizationContext : SynchronizationContext
|
|
{
|
|
public override SynchronizationContext CreateCopy()
|
|
{
|
|
return new BackgroundSynchronizationContext();
|
|
}
|
|
}
|
|
|
|
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); //任务队列
|
|
private readonly int _maxDegreeOfParallelism; //最大并发线程数
|
|
private int _runningTasks = 0; //当前正在运行的线程数
|
|
private readonly object _lock = new object(); //用于线程同步
|
|
|
|
public TaskSchedulerUtil(int maxDegreeOfParallelism)
|
|
{
|
|
if (maxDegreeOfParallelism < 1)
|
|
throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism));
|
|
_maxDegreeOfParallelism = maxDegreeOfParallelism;
|
|
}
|
|
// 将任务加入队列
|
|
protected override void QueueTask(Task task)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_tasks.AddLast(task); // 将任务加入队列
|
|
TryExecuteNextTask(); // 尝试执行下一任务
|
|
}
|
|
}
|
|
// 尝试执行下一个任务
|
|
private void TryExecuteNextTask()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
// 如果当前运行的线程数小于最大并发数,且队列中还有任务
|
|
while (_runningTasks < _maxDegreeOfParallelism && _tasks.Count > 0)
|
|
{
|
|
var task = _tasks.First.Value;
|
|
_tasks.RemoveFirst(); // 取出一个任务
|
|
_runningTasks++; // 增加正在运行的任务计数
|
|
|
|
// 在线程池中运行任务
|
|
ThreadPool.QueueUserWorkItem(_ =>
|
|
{
|
|
try
|
|
{
|
|
ExecuteTaskWithoutWinFormsContext(task);
|
|
}
|
|
finally
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_runningTasks--; // 减少正在运行的任务计数
|
|
TryExecuteNextTask(); // 尝试执行下一个任务
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// 尝试在线程中直接运行任务(不支持)
|
|
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
|
{
|
|
// 不允许直接在线程中运行任务(可以根据需求修改)
|
|
return false;
|
|
}
|
|
// 返回已计划的任务(用于调试)
|
|
protected override IEnumerable<Task> GetScheduledTasks()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
return _tasks.ToList();
|
|
}
|
|
}
|
|
}
|
|
}
|