C# 的TaskScheduler

07-10 1248阅读

        在C#中,TaskScheduler 是一个抽象类,用于控制任务的执行方式,特别是它们如何被安排到线程池中的线程上执行。

C# 的TaskScheduler
(图片来源网络,侵删)

TaskScheduler 负责将 Task 对象排队并决定何时、以何种方式执行这些任务。

TaskScheduler 的作用

  • 调度任务:将任务分配给线程池中的线程执行。
  • 控制并发:通过限制同时执行的任务数量来控制并发级别。
  • 异常处理:虽然不是直接由 TaskScheduler 处理异常,但它通过控制任务的执行环境间接影响了异常的处理方式。

    如何使用 TaskScheduler

            通常,我们不需要直接创建 TaskScheduler 的实例,因为 Task 类和 TaskFactory 类提供了足够的方法来使用默认的 TaskScheduler(通常是 TaskScheduler.Default,它对应于线程池)。

            然而,在某些情况下,你可能需要自定义 TaskScheduler 来满足特定的需求。

    示例:使用默认的 TaskScheduler

            大多数情况下,你不需要显式地使用 TaskScheduler,因为 Task.Run 和 Task.Factory.StartNew(当不提供 TaskScheduler 时)默认使用 TaskScheduler.Default。

    // 使用 Task.Run 启动一个后台任务  
    Task.Run(() =>  
    {  
        // 这里执行耗时操作  
        Console.WriteLine("任务正在后台执行...");  
        Thread.Sleep(1000); // 模拟耗时操作  
        Console.WriteLine("任务完成.");  
    });  
      
    // 等待任务完成(仅为了示例,实际代码中通常不会这样做)  
    Task.Delay(2000).Wait(); // 确保主线程等待足够长的时间以看到后台任务的结果
    示例:自定义 TaskScheduler

            如果你需要自定义任务调度逻辑(例如,限制并发任务的数量),你可以继承 TaskScheduler 并实现其抽象方法。然而,这通常是一个高级用法,需要深入理解多线程编程和 Task 的工作机制。

            这里是一个简化的例子,说明如何继承 TaskScheduler,但请注意,这个例子并没有真正实现任务调度逻辑,而只是展示了如何开始自定义:

    public class LimitedConcurrencyTaskScheduler : TaskScheduler  
    {  
        // 假设我们有一个限制并发数的字段  
        private readonly int _maxConcurrencyLevel;  
        private int _activeTasks;  
      
        public LimitedConcurrencyTaskScheduler(int maxConcurrencyLevel)  
        {  
            _maxConcurrencyLevel = maxConcurrencyLevel;  
        }  
      
        // 省略了 QueueTask, TryExecuteTaskInline, GetScheduledTasks 等方法的实现  
      
        // 这里只是展示如何开始,实际上你需要实现这些方法来控制任务的调度  
        protected override void QueueTask(Task task)  
        {  
            // 这里应该有逻辑来将任务添加到队列中,并根据需要执行它们  
            // 但由于这是示例,我们只是简单地将任务计数增加  
            Interlocked.Increment(ref _activeTasks);  
      
            // 注意:这实际上并没有真正地将任务排队或执行,只是演示  
      
            // 在真实实现中,你应该在这里将任务放入某种队列中,并在有可用线程时执行它们  
        }  
      
        // ... 其他方法的实现  
    }  
      
    // 注意:上面的 LimitedConcurrencyTaskScheduler 只是一个框架,并没有完整实现  
    // 在实际使用中,你需要完整实现所有抽象方法,并根据需要添加额外的逻辑

            由于自定义 TaskScheduler 的实现相对复杂,且容易出错,因此通常建议仅在我们确实需要控制任务执行的细节时才这样做。在大多数情况下,使用默认的 TaskScheduler 或 Task.Run 就足够了。


    补充:

            在C#中,如果有大量task同时执行,假设我们需要限制同时执行的Task的最大数量为5,我们可以通过自定义一个TaskScheduler来实现,但这种方法相对复杂且容易出错。更常见和简单的方法是使用SemaphoreSlim来限制并发量。

       SemaphoreSlim是一个轻量级的信号量,用于控制对共享资源的并发访问。你可以使用它来限制同时执行的Task数量。

            下面是一个使用SemaphoreSlim来限制Task最大执行个数的例子:

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Threading;  
    using System.Threading.Tasks;  
      
    class Program  
    {  
        static SemaphoreSlim semaphore = new SemaphoreSlim(5); // 初始化为5,表示同时允许5个任务执行  
      
        static async Task Main(string[] args)  
        {  
            // 假设我们有10个任务需要执行  
            var tasks = Enumerable.Range(1, 10).Select(async i =>  
            {  
                await semaphore.WaitAsync(); // 等待信号量可用  
                try  
                {  
                    // 执行任务  
                    Console.WriteLine($"任务 {i} 开始执行,当前时间:{DateTime.Now:HH:mm:ss.fff}");  
                    await Task.Delay(1000); // 模拟耗时操作  
                    Console.WriteLine($"任务 {i} 执行完成,当前时间:{DateTime.Now:HH:mm:ss.fff}");  
                }  
                finally  
                {  
                    semaphore.Release(); // 释放信号量  
                }  
            }).ToList();  
      
            // 等待所有任务完成  
            await Task.WhenAll(tasks);  
      
            Console.WriteLine("所有任务执行完成。");  
        }  
    }

             在上面这个例子中,我们创建了一个SemaphoreSlim实例,初始化为5,表示同时最多允许5个任务执行。

            然后,我们创建了10个任务,每个任务在执行前都会调用semaphore.WaitAsync()来等待信号量变得可用。当信号量可用时,任务会继续执行,并在执行完毕后通过semaphore.Release()释放信号量,以便其他任务可以执行。

            这种方法简单且有效,不需要我们深入了解TaskScheduler的内部实现,就可以轻松控制并发量。同时,它也避免了自定义TaskScheduler可能带来的复杂性和潜在的错误。

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]