关于多线程数量控制问题

殘丿__花 2017-02-09 09:21:29
现在做的项目由于数据量巨大,所以采用任务行式。任务直接安装到windows服务中,服务只有一个,但是服务下有多个线程分别处理不同的工作。每一个任务用一个单独的thread轮循。任务基本分为时刻处理的和定时处理的。

现存问题1,定时任务无法长时间休眠,如每月执行的,可能服务在正常跑,但任务却没有执行。
此问题目前处理方法是缩短休眠时间。现在想做一个监控,如果有任务睡死,就唤醒它,这个在不大改动的情况下,怎么实现好?观察者模式?之类的请提供思路。

问题2,对于时时处理的。高峰期可能积压,目前解决方案是手动増加线程,但是低峰时又浪费资源。高峰时大约会积压到每天几十万次甚至更高处理量。尝试过线程池,但感觉还不如开无限个thread处理的快。现在想要有个动态调度的方式,积压一定量増加线程,空闲时销毁线程。


我目前思路是观察者模式,把任务放到集合中,每次任务自已记录状态,集合类每过一段时间询问状态,唤醒或删除(但每个任务至少保留一个)。同时注册个事件,接收任务积压的通知。

不知道这种模式是否可行,请大神提供思路。谢谢!
...全文
962 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
okkk 2017-02-12
  • 打赏
  • 举报
回复
Right: 不用定时器::: 使用的是同步调用执行。楼主需要的是启动线程,那么根据情况,把 tmp.CallBack();改成tmp.CallBack.BeginInvoke即可
/// <summary>
    /// 定时调用指定函数
    /// </summary>
    public class TimerCall
    {
        public static TimerCall m_only = new TimerCall();

        public static TimerCall Current
        {
            get { return m_only; }
        }

        private TimerCall()
        {
            this.Begin();
        }

        class CallInfo : IComparable
        {
            public string Name;
            public double Span;
            public Action CallBack;

            public CallInfo()
            {
            }

            public int CompareTo(object obj)
            {
                return this.Span.CompareTo((obj as CallInfo).Span);
            }
        }

        public void Clear(string name)
        {
            if (string.IsNullOrEmpty(name))
                return;

            lock (toCall)
            {
                foreach(CallInfo aCI in toCall)
                {
                    if (string.Compare(aCI.Name, name) == 0)
                    {
                        toCall.Remove(aCI);
                        return;
                    }
                }
            }
        }

        List<CallInfo> toCall = new List<CallInfo>();
        /// <summary>
        /// 注册任务,覆盖同名回调
        /// </summary>
        /// <param name="name"></param>
        public void Regist(string name, double milSecond, Action callback)
        {
            lock (toCall)
            {
                Clear(name);
                double endT = this.SW.Elapsed.TotalMilliseconds + milSecond;
                CallInfo aCI = new CallInfo() { Name=name, Span=endT, CallBack=callback };
                toCall.Add(aCI);
            }
        }

        /// <summary>
        /// 注册不可取消的回掉
        /// </summary>
        public void Regist(double milSecond, Action callback)
        {
            lock (toCall)
            {
                double endT = this.SW.Elapsed.TotalMilliseconds + milSecond;
                CallInfo aCI = new CallInfo() { Name = "", Span = endT, CallBack = callback };
                toCall.Add(aCI);
            }
        }

        CallInfo[] getNowAll()
        {
            lock (toCall)
            {
                CallInfo[] rT = toCall.ToArray();
                toCall.Clear();
                return rT;
            }
        }

        public double CreatFunction()
        {
            return SW.Elapsed.TotalMinutes;
        }

        System.Diagnostics.Stopwatch SW;
        public void Begin()
        {
            if (SW != null)
            {
                toCall.Clear();
                SW.Stop();
                SW = null;
            }

            SW = new System.Diagnostics.Stopwatch();
            SW.Start();

            Action doThread = new Action(DoTimer);
            doThread.BeginInvoke(null, null);
        }

        public void Stop()
        {
            this.SW = new System.Diagnostics.Stopwatch();
        }


        void DoTimer()
        {
            System.Diagnostics.Stopwatch bindding = this.SW;
            while (bindding == this.SW)
            {
                try
                {
                    CallInfo[] toDO = getNowAll();
                    if (toDO.Length == 0)
                    {
                        System.Threading.Thread.Sleep(600);
                        continue;
                    }
                    double nowTime = bindding.Elapsed.TotalMilliseconds;
                    for (int i = 0; i < toDO.Length; i++)
                    {
                        CallInfo tmp = toDO[i];
                        if (tmp.Span <= nowTime)
                        {
                            try
                            {
                                tmp.CallBack();
                            }
                            catch (Exception ed)
                            {
                                ErrorLog.Current.LogException(100, "定时器调度", ed);
                            }
                        }
                        else
                        {
                            lock (toCall)
                            {
                                toCall.Add(tmp);
                            }
                        }
                    }
                    System.Threading.Thread.Sleep(200);
                }
                catch (Exception eh)
                {
                    ErrorLog.Current.LogException(100, "timercall loop", eh);
                }
            }
        }

       
    }
  • 打赏
  • 举报
回复
引用 楼主 hanxiao1224 的回复:
现存问题1,定时任务无法长时间休眠,如每月执行的,可能服务在正常跑,但任务却没有执行。 此问题目前处理方法是缩短休眠时间。现在想做一个监控,如果有任务睡死,就唤醒它,这个在不大改动的情况下,
灵活配置的、但是很长时间才执行一次的定时任务,不管有多少,都跟定时器的数量无关,都用一个定时器来调度,维护一个简单的定时器(异步启动多种任务)。 难道你用多个定时器来处理多任务?
  • 打赏
  • 举报
回复
关于你的电脑上有多少线程,你打开“程序管理器”看看系统“性能”里边的进程数量和线程数量的描述即可。 我的4核的电脑上,当前有2256个线程(这不算是那些由线程池调度、瞬间几百毫秒就完毕的任务,而是指的是“线程”)。如果对这个数量级感到奇怪,说明你没有理解线程的作用。
wanghui0380 2017-02-09
  • 打赏
  • 举报
回复
请百度“ quartz”
  • 打赏
  • 举报
回复
不管你自己如何发明自己的(比 .net framework 的线程池调度策略低效率的)线程池调度方式,更基本的原则应该注意: 不要在线程中弄无意义的轮询循环。假设有1万任务,那么就像线程池注册1万任务,而不是想当然地、多余地弄出10个线程死循环这1万任务。每一个任务都是尽可能短地在几十毫秒、几百毫秒就结束,这样才能利用好线程调度机制。不要自己创建任何 Thread。
  • 打赏
  • 举报
回复
引用 7 楼 hanxiao1224 的回复:
我现在在测每种任务由一个线程批量去读取任务放到安全队列里,另一个线程去执行。如果队列里达到一定数量,増加执行线程。cpu的两倍是只单任务吗,我只要关心执行方法的线程数就行了,不用考虑其他任务的线程数吗?(没有复杂算法)
你是想自己发明一个线程池,取代 .net framework 的线程池? 一定要自己发明的话,先参考看看 .net 的线程池的源代码即可。
殘丿__花 2017-02-09
  • 打赏
  • 举报
回复
引用 6 楼 dongxinxi 的回复:
每个线程逐个获取并异步处理,完成之后再获取下一个,没有就结束了。保留两个工作线程就好,如果两个都在工作,说明可能还有更多任务,再开线程,直到数量达到CPU核心数的两倍后就不开线程了 以实际测试为准
我现在在测每种任务由一个线程批量去读取任务放到安全队列里,另一个线程去执行。如果队列里达到一定数量,増加执行线程。cpu的两倍是只单任务吗,我只要关心执行方法的线程数就行了,不用考虑其他任务的线程数吗?(没有复杂算法)
  • 打赏
  • 举报
回复
每个线程逐个获取并异步处理,完成之后再获取下一个,没有就结束了。保留两个工作线程就好,如果两个都在工作,说明可能还有更多任务,再开线程,直到数量达到CPU核心数的两倍后就不开线程了 以实际测试为准
Poopaye 2017-02-09
  • 打赏
  • 举报
回复
1、频率很低的定时任务应该考虑用at命令启动 2、不清楚为何增加线程能解决问题,CPU运算能力就这么多,8核的CPU开16个线程反而还要增加切换的开销。估计八成是线程方法阻塞导致,设计上的失败。
  • 打赏
  • 举报
回复
长睡眠时间任务用定时器就好了,你只要处理好(包括异常)就不会有问题 实时任务,你开再多的线程也仍然会会有积压的,并且到后面不仅不会变快,反而会越来越慢直至把程序拖崩 建议的做法,加一个中间件程序(MSMQ或者其他开源队列都行),实时任务全部丢给它,你的服务只需要从中按顺序批量获取处理就好(具体量可以测试几次,比如可设成CPU核心数的两倍,建议用线程池
xdashewan 2017-02-09
  • 打赏
  • 举报
回复
我觉得只要根据数量合理分配好线程池就足够了
殘丿__花 2017-02-09
  • 打赏
  • 举报
回复
还有对于积压,怎么合理分配线程数量?
殘丿__花 2017-02-09
  • 打赏
  • 举报
回复
任务类大概有二十多个,其中有4.5个是大批量处理的

13,190

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 分析与设计
社区管理员
  • 分析与设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧