实现一个所有任务都是按各自自定义的时间间隔周期性执行的线程池

1 篇文章 0 订阅
1 篇文章 0 订阅

在最近的一个项目中,经常有非常多个数据报文需要按各自的特定周期发送,而且用户可以控制是否继续发送、发送多少次、发送的时间周期等,如果每个发送作业都开一个线程,对机器的开销会特别大,而如果使用C#自带的线程池就不能很好的实现这些功能。基于此,需要自己实现一个简单的线程池。

下面这个自定义线程池的特点是,可执行多个周期性作业,每个作业可以由用户自己控制是否继续执行、加入或离开线程池、实时改变执行周期、执行次数、暂停执行和继续执行、停止线程池和重启线程池。并且对周期性作业的准时性做了优化。不足是暂时还未实现线程池的可伸缩性,线程数量是固定的。不过这只需要做较小改动就可以实现。

实现如下:

1、周期性作业类

/// <summary>
    /// 时间作业的具体作业方法的委托
    /// </summary>
    public delegate void TaskFunc();

    /// <summary>
    /// 时间作业
    /// <note>成员包括剩余做几次、是否做、每次作业间隔多久、最后一次作业的时间滴答数</note>
    /// </summary>
    public class TimeTask
    {
        private int _TaskID = -1;
        private int _LeftCount = -1;            //剩余做几次,-1表示一直做
        private bool _IsDoing = true;                //是否执行任务的标记
        private bool _IsInPool = true;                //是否在池中的标记
        private long _Interval = 20000000;      //时间间隔 毫微秒(10^-4ms)
        private long _NextRunStartTime = 0;          //下一次计划作业时间滴答数
        private long _RanCount = 0;                 //作业已执行次数
        private long _AverageRunTime = 0;           //作业平均执行时间
        private TaskFunc _Func;                 //作业方法

        public int TaskID { get { return _TaskID; } set { _TaskID = value; } }
        public int LeftCount { get { return _LeftCount; } set { _LeftCount = value; } }
        public bool IsDoing { get { return _IsDoing; } set { _IsDoing = value; } }
        public bool IsInPool { get { return _IsInPool; }  set { _IsInPool = value; } }
        public long Interval { get { return _Interval; } set { _Interval = value; } }
        public long NextRunStartTime { get { return _NextRunStartTime; } }
        public long AverageRunTime { get { return _AverageRunTime; } }
        public TaskFunc Func
        {
            get { return _Func; }
            set { _Func = value; }
        }
        //执行作业
        public void run()
        {
            if (_LeftCount != 0 && _Func != null)
            {
                long _LastRunStartTime = DateTime.Now.Ticks;
                _Func();
                long _LastRunFinishTime = DateTime.Now.Ticks;
                long _NowRunTime = (_LastRunFinishTime - _LastRunStartTime);
                _NextRunStartTime = _LastRunFinishTime + _Interval - _NowRunTime;
                _AverageRunTime = (_AverageRunTime * _RanCount + _NowRunTime) / (++_RanCount);
                //System.Console.Out.WriteLine("当前耗时: " + _NowRunTime / 10000 + "ms");
                //System.Console.Out.WriteLine("平均耗时: " + _AverageRunTime / 10000 + "ms");
                if (_LeftCount > 0)
                    _LeftCount--;
            }   
        }
        //从线程池中拆卸此作业
        public void dropFromPool()
        {
            _IsInPool = false;
        }
        //停止执行此任务
        public void stop()
        {
            _IsDoing = false;
        }
        //重新启动此任务
        public void restart()
        {
            _IsDoing = true;
        }
        //重载equal
        public override bool Equals(object obj)
        {
            try
            {
                TimeTask _task = (TimeTask)obj;
                return _TaskID == _task.TaskID;
            }
            catch (Exception) { return false; }
        }
    }

2、自定义线程池类

public class MyThreadPool
    {
        private static int _WORKNUM = 5;                                                //线程池线程数
        private static long _LITTLETIMESPAN = 50000;                                    //小时间间隔 毫微秒(10^-4ms)
        private static int _TASKOFFSETFACTOR = 3;                                       //作业排队因子
        private static List<TimeTask> _TaskList = new List<TimeTask>();                 //作业队列
        private static WorkThread[] _WorksThread = new WorkThread[_WORKNUM];            //线程池中的所有线程
        private static bool _isRun = true;                                              //控制线程池运行与否的标志
        private static int _NextTaskID = 0;                                             //下一个作业的ID

        #region 获取单例
        private static readonly MyThreadPool _instance = new MyThreadPool();
        static MyThreadPool()
        {
            _isRun = true;
            for (int _i = 0; _i < _WORKNUM; _i++)
            {
                _WorksThread[_i] = new WorkThread();
                _WorksThread[_i].start();
            }
        }
        private MyThreadPool()
        {
            _isRun = true;
            for (int _i = 0; _i < _WORKNUM; _i++)
            {
                _WorksThread[_i] = new WorkThread();
                _WorksThread[_i].start();
            }
        }
        public static MyThreadPool Instance
        {
            get
            {
                return _instance;
            }
        }
        #endregion

        //增加作业,并提供作业ID
        public void addTask(TimeTask _task)
        {
            //作业需要先执行一次
            _task.run();
            _task.TaskID = _NextTaskID++;
            lock (_TaskList)
            {
                _TaskList.Add(_task);
            }
        }
        //开启线程池中所有线程
        public void startAllThread()
        {
            if (!_isRun)
            {
                _isRun = true;
                for (int _i = 0; _i < _WORKNUM; _i++)
                {
                    _WorksThread[_i].start();
                }
            }
        }
        //自然停止所有线程(等待所有作业单个运行完毕)
        public void stopAllThread()
        {
            _isRun = false;
        }
        //清空作业列表
        public void clearTaskList()
        {
            lock (_TaskList)
            {
                _TaskList.Clear();
            }
        }
        //删除某一项作业
        public bool removeTask(int _TaskID)
        {
            lock (_TaskList)
            {
                foreach(TimeTask _iter in _TaskList)
                {
                    if (_iter.TaskID == _TaskID)
                        return _TaskList.Remove(_iter);
                }
            }
            return false;
        }


        /// <summary>
        /// 线程池中的单个线程
        /// <note>MyThreadPool的内部类</note>
        /// </summary>
        private class WorkThread
        {
            //线程实体
            private Thread _thisThread; 
                                                            
            public WorkThread()
            {
            }
            //开启线程
            public void start()
            {
                if (_thisThread == null)
                {
                    _thisThread = new Thread(_job);
                    _thisThread.Start();
                    return;
                }
                if (_thisThread.IsAlive) { }
                else
                {
                    _thisThread = new Thread(_job);
                    _thisThread.Start();
                    return;
                }
            }
            //线程执行方法:取出作业并执行,执行完毕后再次放回作业
            private void _job()
            {
                //判断线程池是否要运行
                while (_isRun)
                {
                    TimeTask _nowTask = null;
                    //取出列表中第一个任务
                    lock (_TaskList)
                    {
                        if (_TaskList.Count != 0)
                        {
                            _nowTask = _TaskList.First();
                            _TaskList.RemoveAt(0);
                        }
                    }
                    //如果作业队列为空,则挂起线程100ms后重新取作业
                    if (_nowTask == null) { Thread.Sleep((int)(_LITTLETIMESPAN/10000));continue; }
                    else
                    {
                        //拆卸作业,取出作业不往队列中再次添加
                        if (!_nowTask.IsInPool)
                        {
                            //
                        }
                        //剩余作业次数不为0且作业标记为执行
                        else if (_nowTask.LeftCount != 0 && _nowTask.IsDoing)
                        {
                            //如果当前时间小于计划执行时间的负误差,就将作业排到队列末尾
                            if (DateTime.Now.Ticks < _nowTask.NextRunStartTime - _LITTLETIMESPAN)
                            {
                                lock (_TaskList)
                                {
                                    _TaskList.Add(_nowTask);
                                }
                            }
                            //如果当前时间大于等于计划执行时间,就执行作业,然后将作业排到队列末尾
                            else if (DateTime.Now.Ticks >= _nowTask.NextRunStartTime)
                            {
                                _nowTask.run();
                                lock (_TaskList)
                                {
                                    _TaskList.Add(_nowTask);
                                }
                            }
                            //如果当前时间处于计划执行时间的负误差之中,就将作业排到队列中靠前的位置(取决于_TASKOFFSETFACTOR)
                            else
                            {
                                lock (_TaskList)
                                {
                                    if (_TaskList.Count < _WORKNUM)
                                    {
                                        _TaskList.Add(_nowTask);
                                    }
                                    else
                                    {
                                        _TaskList.Insert(_TaskList.Count / _TASKOFFSETFACTOR, _nowTask);
                                    }
                                }
                            }
                        }
                        //如果当前作业剩余执行次数为零或者作业标记为不执行,就将此作业排到队列末尾
                        else
                        {
                            lock (_TaskList)
                            {
                                _TaskList.Add(_nowTask);
                            }
                        }
                    }
                }
            }
        }

    }

3、测试结果总结:

编写了一个小Demo对此线程池进行了测试,测试使用了10个周期性作业、每个作业的执行周期和执行时间都不相同。

以下是测试数据:(数据项从左至右依次为作业开始执行时间、作业名称、作业模拟执行时间【调用sleep】,作业名称的数字位也代表执行周期,如Task5代表此作业5秒执行一次)

12:23:06:903: Task1 --> 50ms
12:23:06:906: Task2 --> 20ms
12:23:06:908: Task3 --> 500ms
12:23:06:909: Task4 --> 200ms
12:23:06:911: Task5 --> 10ms
12:23:06:912: Task6 --> 30ms
12:23:06:914: Task7 --> 100ms
12:23:06:916: Task8 --> 40ms
12:23:06:918: Task9 --> 60ms
12:23:06:920: Task10 --> 70ms
12:23:08:66: Task1 --> 50ms
12:23:09:67: Task1 --> 50ms
12:23:09:155: Task2 --> 20ms
12:23:10:312: Task1 --> 50ms
12:23:10:348: Task3 --> 500ms
12:23:11:235: Task4 --> 200ms
12:23:11:312: Task1 --> 50ms
12:23:11:486: Task2 --> 20ms
12:23:12:38: Task5 --> 10ms
12:23:12:312: Task1 --> 50ms
12:23:12:986: Task6 --> 30ms
12:23:13:410: Task1 --> 50ms
12:23:13:950: Task3 --> 500ms
12:23:14:386: Task7 --> 100ms
12:23:14:409: Task1 --> 50ms
12:23:14:842: Task2 --> 20ms
12:23:15:231: Task8 --> 40ms
12:23:15:409: Task1 --> 50ms
12:23:15:429: Task4 --> 200ms
12:23:16:16: Task9 --> 60ms
12:23:16:480: Task1 --> 50ms
12:23:16:923: Task2 --> 20ms
12:23:16:950: Task3 --> 500ms
12:23:17:126: Task10 --> 70ms
12:23:17:415: Task5 --> 10ms
12:23:17:516: Task1 --> 50ms
12:23:18:750: Task1 --> 50ms
12:23:18:985: Task6 --> 30ms
12:23:19:490: Task2 --> 20ms
12:23:19:578: Task4 --> 200ms
12:23:19:988: Task3 --> 500ms

发现作业执行的周期准时性存在较小的误差,但都保持在毫秒级,满足当前项目的需求。



 



  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值