有这么一个场景:
假设有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>