C# 实现 异步任务自动运行控制

有这么一个场景:

假设有10000个相同逻辑的任务需要执行,而且希望是在后台异步运行;

然后在执行过程中,由于一些不确定的因素(内存、网络 等),限制了同时运行的数量(这里先假定可以同时运行50个任务);

最后关键的是,每一个任务所需的参数都不尽相同!

这就好比,有10000个工人要去做工,干的活都一样,但每个人所带的工具却不尽相同,然后又由于一些条件的限制,每次只允许50个工人在同时工作;该怎么办呢?

思路:

在现实的情况中,我们需要有一个工人调度的管理员,来随时调度这10000个带有不同工具的工人,

最开始先派上50个工人去做工,过个三五分钟就去查看一下工人的做工情况,

看了几次后,发现有20个工人已经做完了,于是就把这已经完成的20个工人撤下来,再补上新的20个工人,

又过了一段时间,发现又有15个工人做完了,于是又把这已完成的15个工人撤下来,再补上新的15个工人,

如此循环往复,只要有完成的,就撤走,换上新的...... 直到10000个工人都完成做工。

如此一来,便可以发挥最大的利用效率,总是在 允许 可做工的 范围内,拥有最多的做工人数,直到全部做工完成。

我在实际工作中的应用:

在下是做数据采集的(网络爬虫),经常会遇到这么一个情况,

发起的采集请求都是后台异步的,采集的数据有成百上千页,采集每一页的数据所发起的请求参数又不相同,然后网站又不稳定,不能同时请求太多,

于是就自己琢磨了一个功能任务自动控制类。

下面是代码,分享出来,给大家参考:

注意:看我的代码注释,帮助你理解我的实现思路,给你一个参考。

自动功能控制类:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleAppDemo
{
    /// <summary>
    /// 自动功能控制类
    /// </summary>
    public sealed class AutoActionControl
    {
        /// <summary>
        /// AutoActionControl 无参构造函数
        /// </summary>
        public AutoActionControl() { }

        /// <summary>
        /// AutoActionControl 构造函数
        /// </summary>
        /// <param name="action">
        /// <para>Func 委托,功能函数;传入一个 object 参数,返回一个 Task 结果</para>
        /// <para>注:这个函数最好加上 try{} catch (Exception ex) { //这里可以写异常日志 }</para>
        /// </param>
        public AutoActionControl(Func<object, Task> action)
        {
            AutoAction = action;
        }

        /// <summary>
        /// AutoActionControl 构造函数
        /// </summary>
        /// <param name="action">
        /// <para>Func 委托,功能函数;传入一个 object 参数,返回一个 Task 结果</para>
        /// <para>注:这个函数最好加上 try{} catch (Exception ex) { //这里可以写异常日志 }</para>
        /// </param>
        /// <param name="paramsList">
        /// <para>参数集合 【 AutoAction 所需要的参数 】</para>
        /// <para>当 AutoAction 功能函数不需要参数时,此属性赋值为 null (也可以不用设置,默认就是 null 值)</para>
        /// <para>
        /// 当 MaxExecuteCount != 0 时(有限执行),且 AutoAction 功能函数需要参数,
        /// 此属性赋值为一个参数集合,集合的元素个数(Count 值)须等于 MaxExecuteCount
        /// </para>
        /// <para>当 MaxExecuteCount == 0 时(无限执行),此属性无意义;这种情况应当直接把“传参过程”写在 AutoAction 功能函数里</para>
        /// </param>
        public AutoActionControl(Func<object, Task> action, List<object> paramsList)
        {
            AutoAction = action;
            ParamsList = paramsList;
        }

        /// <summary>
        /// AutoActionControl 构造函数
        /// </summary>
        /// <param name="action">
        /// <para>Func 委托,功能函数;传入一个 object 参数,返回一个 Task 结果</para>
        /// <para>注:这个函数最好加上 try{} catch (Exception ex) { //这里可以写异常日志 }</para>
        /// </param>
        /// <param name="paramsList">
        /// <para>参数集合 【 AutoAction 所需要的参数 】</para>
        /// <para>当 AutoAction 功能函数不需要参数时,此属性赋值为 null (也可以不用设置,默认就是 null 值)</para>
        /// <para>
        /// 当 MaxExecuteCount != 0 时(有限执行),且 AutoAction 功能函数需要参数,
        /// 此属性赋值为一个参数集合,集合的元素个数(Count 值)须等于 MaxExecuteCount
        /// </para>
        /// <para>当 MaxExecuteCount == 0 时(无限执行),此属性无意义;这种情况应当直接把“传参过程”写在 AutoAction 功能函数里</para>
        /// </param>
        /// <param name="maxExecuteCount">
        /// <para>最大执行数量;也就是 功能函数最多运行几次</para>
        /// <para>如果设为 0,则不计总量的一直运行;属性 RunAlready 和 RunCompleted 也将没有意义。</para>
        /// </param>
        /// <param name="simultaneousExecute">同时执行数量;也就是 最多同时运行几个功能函数</param>
        /// <param name="scanningFrequency">扫描频率,单位:毫秒(每多少毫秒扫描一次)</param>
        public AutoActionControl(Func<object, Task> action, List<object> paramsList,
                                 int maxExecuteCount, int simultaneousExecute, int scanningFrequency)
        {
            AutoAction = action;
            ParamsList = paramsList;
            MaxExecuteCount = maxExecuteCount;
            SimultaneousExecute = simultaneousExecute;
            ScanningFrequency = scanningFrequency;
        }


        /// <summary>
        /// 声明一个加锁对象,用来控制同时执行的任务数量
        /// </summary>
        private readonly object _lockTaskList = new object();

        /// <summary>
        /// Task 任务集合
        /// </summary>
        private readonly List<Task> _taskList = new List<Task>();

        /// <summary>
        /// 声明一个 bool 变量,用来指示是否停止任务的运行。true/false,是/否
        /// </summary>
        private bool _isStopTask = true;


        #region 核心属性

        /// <summary>
        /// 正在运行的数量(该属性值 只会大于等于 0 且 小于等于 SimultaneousExecute 值)
        /// </summary>
        public int RunningCount
        {
            get
            {
                return _taskList.Count;
            }
        }

        /// <summary>
        /// 指示自动功能是否已全部完成【在调用 StartExecute() 方法前调用该属性,无意义】
        /// </summary>
        public bool IsFinish
        {
            get
            {
                if (MaxExecuteCount != 0)
                {
                    return (RunCompleted >= MaxExecuteCount);
                }
                else
                {
                    return (_isStopTask == true && _taskList.Count == 0);
                }
            }
        }

        /// <summary>
        /// <para>Func 委托,功能函数;传入一个 object 参数,返回一个 Task 结果</para>
        /// <para>注:这个函数最好加上 try{} catch (Exception ex) { //这里可以写异常日志 }</para>
        /// </summary>
        public Func<object, Task> AutoAction { get; set; } = null;

        /// <summary>
        /// <para>参数集合 【 AutoAction 所需要的参数 】</para>
        /// <para>当 AutoAction 功能函数不需要参数时,此属性赋值为 null (也可以不用设置,默认就是 null 值)</para>
        /// <para>
        /// 当 MaxExecuteCount != 0 时(有限执行),且 AutoAction 功能函数需要参数,
        /// 此属性赋值为一个参数集合,集合的元素个数(Count 值)须等于 MaxExecuteCount
        /// </para>
        /// <para>当 MaxExecuteCount == 0 时(无限执行),此属性无意义;这种情况应当直接把“传参过程”写在 AutoAction 功能函数里</para>
        /// </summary>
        public List<object> ParamsList { get; set; } = null;



        private int runAlready = 0;

        /// <summary>
        /// <para>已经运行的数量(只要是已开始执行的任务,无需完成,该属性值就增加计数)</para>
        /// <para>当 MaxExecuteCount = 0 时,该属性无意义,返回 -1</para>
        /// </summary>
        public int RunAlready
        {
            get
            {
                return MaxExecuteCount != 0 ? runAlready : -1;
            }
            private set
            {
                runAlready = value;
            }
        }


        private int runCompleted = 0;

        /// <summary>
        /// <para>运行已完成的数量(只有运行已完成的任务,该属性值才会增加计数)</para>
        /// <para>当 MaxExecuteCount = 0 时,该属性无意义,返回 -1</para>
        /// </summary>
        public int RunCompleted
        {
            get
            {
                return MaxExecuteCount != 0 ? runCompleted : -1;
            }
            private set
            {
                runCompleted = value;
            }
        }


        private int maxExecuteCount = 100;

        /// <summary>
        /// <para>最大执行数量(默认 100 个);也就是 功能函数最多运行几次</para>
        /// <para>如果设为 0,则不计总量的一直运行;属性 RunAlready 和 RunCompleted 也将没有意义。</para>
        /// </summary>
        public int MaxExecuteCount
        {
            get
            {
                return maxExecuteCount;
            }
            set
            {
                maxExecuteCount = value < 0 ? 100 : value;
            }
        }


        private int simultaneousExecute = 10;

        /// <summary>
        /// 同时执行数量(默认 10 个);也就是 最多同时运行几个功能函数 (类似池的概念,有空闲的就自动加入下一个)
        /// </summary>
        public int SimultaneousExecute
        {
            get
            {
                if (simultaneousExecute > MaxExecuteCount)
                {
                    simultaneousExecute = MaxExecuteCount;
                }
                return simultaneousExecute;
            }
            set
            {
                simultaneousExecute = value < 1 ? 10 : value;
            }
        }


        private int scanningFrequency = 500;

        /// <summary>
        /// 扫描频率,单位:毫秒(每多少毫秒扫描一次)。默认:500
        /// </summary>
        public int ScanningFrequency
        {
            get
            {
                return scanningFrequency;
            }
            set
            {
                scanningFrequency = value < 1 ? 500 : value;
            }
        }

        #endregion


        /// <summary>
        /// 任务调度(控制同时执行数)
        /// </summary>
        private void TaskScheduling()
        {
            if (MaxExecuteCount != 0)
            {
                if (RunAlready >= MaxExecuteCount)
                {
                    _isStopTask = true;
                    if (_taskList.Count < 1)
                    {
                        // 已经执行完毕
                        return;
                    }
                }
            }

            if (_taskList.Count > 0)
            {
                if (_taskList.Count <= SimultaneousExecute)
                {
                    int i = 0;
                    while (i < _taskList.Count)
                    {
                        if (_taskList[i].IsCompleted)
                        {
                            _taskList[i].Dispose();
                            _taskList.Remove(_taskList[i]);
                            if (MaxExecuteCount != 0)
                            {
                                RunCompleted++;
                            }
                        }
                        else
                        {
                            i++;
                        }
                    }

                    JoinExecuteTaskAsync().Wait();
                }
            }
            else
            {
                JoinExecuteTaskAsync().Wait();
            }
        }

        /// <summary>
        /// 加入执行任务
        /// </summary>
        private async Task JoinExecuteTaskAsync()
        {
            if (!_isStopTask)
            {
                int residue = SimultaneousExecute - _taskList.Count; //剩余数
                if (MaxExecuteCount != 0)
                {
                    while (residue > 0 && RunAlready < MaxExecuteCount)
                    {
                        _taskList.Add(await Task.Factory.StartNew(AutoAction, ParamsList?[RunAlready]));
                        RunAlready++;
                        residue--;
                    }
                }
                else
                {
                    while (residue > 0)
                    {
                        _taskList.Add(await Task.Factory.StartNew(AutoAction, null));
                        residue--;
                    }
                }
            }
        }


        /// <summary>
        /// 启动执行(该方法会重新启动执行)
        /// </summary>
        public AutoActionControl StartExecute()
        {
            if (AutoAction == null)
            {
                throw new Exception("不存在自动功能。");
            }

            if (MaxExecuteCount != 0)
            {
                if (ParamsList != null && ParamsList.Count != MaxExecuteCount)
                {
                    throw new Exception("功能函数的参数集合不符合规格。");
                }
            }

            _isStopTask = false;
            RunAlready = 0;
            RunCompleted = 0;
            _taskList.Clear();

            Task.Run(async () =>
            {
                while (true)
                {
                    lock (_lockTaskList)
                    {
                        TaskScheduling();
                    }
                    if (_taskList.Count < 1)
                    {
                        break;
                    }
                    await Task.Delay(ScanningFrequency);
                }
            });

            return this;
        }

        /// <summary>
        /// 停止执行(正常情况会返回 true,无实际意义)
        /// </summary>
        /// <returns></returns>
        public async Task<bool> StopExecuteAsync()
        {
            _isStopTask = true;

            await Task.Run(() =>
            {
                while (true)
                {
                    if (_taskList.Count < 1)
                    {
                        break;
                    }
                }
            });

            return true;
        }

        /// <summary>
        /// 继续执行
        /// </summary>
        public void GoOnExecute()
        {
            if (AutoAction == null)
            {
                throw new Exception("不存在自动功能。");
            }

            if (MaxExecuteCount != 0)
            {
                if (ParamsList != null && ParamsList.Count != MaxExecuteCount)
                {
                    throw new Exception("功能函数的参数集合不符合规格。");
                }

                if (RunAlready >= MaxExecuteCount)
                {
                    _isStopTask = true;
                    return;
                }
            }

            _isStopTask = false;

            Task.Run(async () =>
            {
                while (true)
                {
                    lock (_lockTaskList)
                    {
                        TaskScheduling();
                    }
                    if (_taskList.Count < 1)
                    {
                        break;
                    }
                    await Task.Delay(ScanningFrequency);
                }
            });
        }


        /// <summary>
        /// 等待全部任务完成
        /// <para>注:使用该方法必须等待该方法的完成,否则使用无效</para>
        /// <para>当 MaxExecuteCount = 0 时,该方法无意义,结果为 false</para>
        /// </summary>
        /// <param name="pollingTime">轮询时间,单位 ms 毫秒,默认 500</param>
        /// <returns></returns>
        public async Task<bool> WaitingAllFinishAsync(int pollingTime = 500)
        {
            if (MaxExecuteCount == 0)
            {
                return false;
            }

            return await Task.Run(async () =>
            {
                while (true)
                {
                    await Task.Delay(pollingTime);
                    if (this.IsFinish)
                    {
                        break;
                    }
                }
                return true;
            });
        }
    }
}

对上面自动功能控制类的使用:

注释部分是另一种使用情况 ; 最后的是一个“过桥”的示例

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleAppDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Action 委托,功能函数
            async Task func(object param)
            {
                try
                {
                    await Task.Delay(3 * 1000);
                    Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.ffff")} ==> "
                                    + $"耗时操作 ==> 参数:{(param == null ? "null 无用" : param.ToString())}\n");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.ffff")} ==> 异常信息:{ex.Message}\n");
                }
            }

            // 模拟参数
            List<object> paramsList = new List<object>();
            for (int i = 1; i <= 8; i++)
            {
                paramsList.Add(i + 10);
            }

            #region MaxExecuteCount != 0 示例

            // 初始化 自动功能控制类
            AutoActionControl autoAction = new AutoActionControl(func, paramsList)
            {
                SimultaneousExecute = 3,    // 最多同时运行 SimultaneousExecute 个数量(类似池的概念,有空闲的就自动加入下一个)

                // 功能函数最大运行 MaxExecuteCount 次数(当前功能函数最多运行几次);
                // 如果设为 0,则不计总量的一直运行;属性 RunAlready 和 RunCompleted 也将没有意义。
                MaxExecuteCount = paramsList.Count,
            };

            // IsFinish、RunAlready、RunCompleted 属性用法示例
            Task.Run(async () =>
            {
                while (true)
                {
                    await Task.Delay(1000);
                    if (autoAction.IsFinish)
                    {
                        Console.WriteLine($"autoAction.IsFinish = {autoAction.IsFinish}\n"
                                        + $"autoAction.RunAlready = {autoAction.RunAlready}\n"
                                        + $"autoAction.RunCompleted = {autoAction.RunCompleted}\n");
                        break;
                    }
                    else
                    {
                        Console.WriteLine($"autoAction.IsFinish = {autoAction.IsFinish}\n"
                                        + $"autoAction.RunAlready = {autoAction.RunAlready}\n"
                                        + $"autoAction.RunCompleted = {autoAction.RunCompleted}\n");
                    }
                }
            });

            autoAction.StartExecute();                  // 启动执行

            System.Threading.Thread.Sleep(5 * 1000);    // 模拟运行 5 秒后,
            var task = autoAction.StopExecuteAsync();   // 停止自动运行
            task.Wait();
            bool res = task.Result;                     // 得到一个返回值(正常情况会返回 true,无实际意义)

            System.Threading.Thread.Sleep(10 * 1000);   // 模拟停止 10 秒后,
            autoAction.GoOnExecute();                   // 再继续自动运行

            System.Threading.Thread.Sleep(20 * 1000);   // 模拟运行 20 秒后,功能函数全部运行完毕,
            Console.WriteLine("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
            Console.WriteLine("模拟运行 20 秒后,功能函数全部运行完毕,\n然后,再次调用 StartExecute() 会重新启动并开始运行");
            Console.WriteLine("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            autoAction.StartExecute();                  // 然后,再次调用 StartExecute() 会重新启动并开始运行

            #endregion

            #region MaxExecuteCount == 0 示例

            //AutoActionControl autoAction = new AutoActionControl(func)
            //{
            //    SimultaneousExecute = 2,    // 最多同时运行 SimultaneousExecute 个数量(类似池的概念,有空闲的就自动加入下一个)

            //    // 功能函数最大运行 MaxExecuteCount 次数(当前功能函数最多运行几次);
            //    // 如果设为 0,则不计总量的一直运行;属性 RunAlready 和 RunCompleted 也将没有意义。
            //    MaxExecuteCount = 0
            //};

             IsFinish、RunAlready、RunCompleted 属性用法示例
            //Task.Run(async () =>
            //{
            //    while (true)
            //    {
            //        await Task.Delay(1000);
            //        if (autoAction.IsFinish)
            //        {
            //            Console.WriteLine($"autoAction.IsFinish = {autoAction.IsFinish}\n"
            //                            + $"autoAction.RunAlready = {autoAction.RunAlready}\n"
            //                            + $"autoAction.RunCompleted = {autoAction.RunCompleted}\n");
            //            break;
            //        }
            //        else
            //        {
            //            Console.WriteLine($"autoAction.IsFinish = {autoAction.IsFinish}\n"
            //                            + $"autoAction.RunAlready = {autoAction.RunAlready}\n"
            //                            + $"autoAction.RunCompleted = {autoAction.RunCompleted}\n");
            //        }
            //    }
            //});

            //autoAction.StartExecute();                  // 启动执行

            //System.Threading.Thread.Sleep(5 * 1000);    // 模拟运行 5 秒后,
            //var task = autoAction.StopExecuteAsync();   // 停止自动运行
            //task.Wait();
            //bool res = task.Result;                     // 得到一个返回值(正常情况会返回 true,无实际意义)

            //System.Threading.Thread.Sleep(5 * 1000);    // 模拟停止 5 秒后,
            //autoAction.GoOnExecute();                   // 再继续自动运行

            //System.Threading.Thread.Sleep(5 * 1000);    // 模拟运行 5 秒后,
            //var task2 = autoAction.StopExecuteAsync();  // 最终停止
            //task2.Wait();
            //bool res2 = task2.Result;                   // 得到一个返回值(正常情况会返回 true,无实际意义)

            #endregion



            // 自动任务控制测试( 过桥 )
            {
                Task.Run(async () =>
                {
                    List<object> list = new List<object>();
                    for (int i = 0; i < 10; i++)
                    {
                        list.Add(i + 1);
                    }

                    static async Task func(object index)
                    {
                        Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffff} 第 {(int)index} 个人 上桥");
                        await Task.Delay(1000);

                        Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffff} 第 {(int)index} 个人 正在过桥");
                        int ss = new Random().Next(3, 9);
                        await Task.Delay(ss * 1000);
                        Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffff} 第 {(int)index} 个人 已过桥 - {ss + 1}s");
                    }

                    var autoAction = new AutoActionControl(func, list, list.Count, 3, 200).StartExecute();

                    await autoAction.WaitingAllFinishAsync(200);

                    Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffff} 所有人都已过桥");
                });
            }

            Console.ReadKey();
        }
    }
}

上面的代码是在下自己的一个实现思路,也很简单,但很实用。你可以直接复制代码,自己建一个控制台项目,尝试运行看看。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值