前言

此文并不是说要完全放弃使用Thread.Sleep,而是要说明在符合哪些情况下使用!

场景

很多时候,我们会需要一个定时服务来处理业务。

但并不是死死的每隔N分钟执行一次那种,而是在一次处理完后,算好下一次处理的时间点。

当到达此时间点,触发程序重新开始执行代码。

普遍做法

  

普遍的情况下,都是使用while(true){Thread.Sleep()}来实现,废话不多话,看代码版本1:

class Program
    {
        static void Main(string[] args)
        {
            var workLists = new List<string>() { "任务1", "任务2", "任务3", "任务4" };
            foreach (var task in workLists)
            {
                var thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Work.DoWork));
                thread.Start(task);
            }
        }
    }
class Work
    {
        public static void DoWork(object target)
        {
            var taskType = target as string;
            var interval = 1 * 60 * 1000;//处理失败,1分钟后重试
            var maxTimes = 5;
            var retryTimes = 0;
            while (true)
            {
                while (retryTimes < maxTimes)
                {
                    var ok = Proccess(taskType);
                    if (ok)
                    {
                        retryTimes = maxTimes;
                    }
                    else
                    {
                        retryTimes++;
                        System.Threading.Thread.Sleep(interval);
                    }
                }

                var tim = GetTotalMillisecondsForNext();//计算离下一次开始处理的时间
                System.Threading.Thread.Sleep(tim);//挂起一段时间后,重新唤醒
                retryTimes = 0;
            }
        }

        private static bool Proccess(string taskType)
        {
            Console.WriteLine("开始执行处理:{0}", taskType);
            return true;
        }

        private static int GetTotalMillisecondsForNext()
        {
            //这里根据自己的业务来决定
            return 2 * 1000;
        }
    }

代码简单易懂。

分析

版本1中,循环强制创建线程,并使用System.Threading.Thread.Sleep(tim)来挂起线程,然后重新唤醒。

这种方式不好之处在于:占用系统线程资源,是一种浪费。如同占着茅坑不拉屎!线程是一种十分宝贵的资源,创建,销毁,切换 都是相当耗性能的。

当Sleep的时候,就等于说:现在我不用,但是你也别想用。你要用?自己去Create一个。

有的人说,Sleep的时候 不占用CPU啊!对,是不占用CPU ,但是占着线程资源,阻碍系统的线程调度!

可以参考下这文章

Threads are a limited resource, they take approximately 200,000 cycles to create and about 100,000 cycles to destroy. By default they reserve 1 megabyte of virtual memory for its stack and use 2,000-8,000 cycles for each context switch. This makes any waiting thread a huge waste.

改进

  使用System.Timers.Timer来改进我们的程序。当执行处理业务的代码时,首先把timer停止,处理完毕后,算好一次执行的时间点,赋给timer并启动,看代码版本2

class Program
    {
        static void Main(string[] args)
        {
            var workLists = new List<string>() { "任务1", "任务2", "任务3", "任务4" };

            Parallel.ForEach(workLists,
                 },
                (task) => { new Work2() { TaskType = task }.DoWork(); });

            Console.ReadLine();
        }

    }
 class Work2
    {
        private Timer _workTimer;
        public string TaskType { get; set; }
        public void DoWork()
        {

            _workTimer = new System.Timers.Timer();
            _workTimer.Interval = ;
            _workTimer.Elapsed += new ElapsedEventHandler(TimerHanlder);
            _workTimer.Start();
        }

        private void TimerHanlder(object sender, ElapsedEventArgs e)
        {
            _workTimer.Stop();

             *  * ;//处理失败,1分钟后重试
            ;
            ;

            while (retryTimes < maxTimes)
            {
                var ok = Proccess();
                if (ok)
                {
                    retryTimes = maxTimes;
                }
                else
                {
                    retryTimes++;
                    System.Threading.Thread.Sleep(interval);
                }

            }

            var times = GetTotalSecondsForNext();
            Console.WriteLine("{0}秒后重新执行", times);
            _workTimer.Interval = times * ;//计算离下一次开始处理的时间
            _workTimer.Start();

        }

        private bool Proccess()
        {
            Console.WriteLine("开始执行处理:{0}", TaskType);
            return true;
        }

        private int GetTotalSecondsForNext()
        {
            //这里根据自己的业务来决定
            ;
        }
    }

特别说明一下:Main方法中的Console.ReadLine();很重要,让主线程处于等待的状态,子线程就可以一直执行下去不中断

总结

1:使用Task,而不是使用new System.Threading.Thread。是否要创建线程,应该让系统来决定,利用可复用资源

2: System.Threading.Thread.Sleep(interval);只合适在 "有限度的 " 循环场景中,比如 最多重试N次、倒计时等等

如果不对之处,请各位斧正!

为什么要放弃使用Thread.Sleep的更多相关文章

  1. 为什么要放弃 JSP?他们终于给出了答案

    前言 以前的项目大多数都是Java程序猿又当爹又当妈,既搞前,又搞后端. 随着时代的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只负责前端的事情,后端工程师只管后端的事情.正 ...

  2. it小小鸟

    It小小鸟观后感 每个人的理想目标都是不同的,很多人有自己的理想.却被困于现实而止步不前.一篇<it小小鸟>让我却懂得,一个人如果想有所作为,就不能止步不前.光有一个远大的理想是然并卵的. ...

  3. The Zero

    TOday is just a alpha, but there isnot 欧米伽. 编程,是你选的,是你学的,也是你喜欢的,更是你追求的.为什么要放弃塔!. 加油! 为自己. #include&l ...

  4. Ueditor上传图片到本地改造到上传图片到云存储

    作为新手说多了都是泪啊!我特别想记录一下作为菜鸟时的坑.看看以后是否会看着笑出来. 为什么要改到云存储上就不说了.好处多多. 视频教程上使用的又拍云同时也提到了七牛云.下来我自己也查了下.又拍云是试用 ...

  5. 从底层角度看ASP.NET-A low-level Look at the ASP.NET...

    从更低的角度 这篇文章在一个底层的角度来关注一个web请求怎样到达asp.net框架,从web服务器,通过ISAPI.看看这些后面发生了什么,让我们停止对asp.net的黑箱猜想.ASP.NET是一个 ...

  6. 所有设计复杂的ORM都是浮云

    很久没有写文章了. 一直很忙,不是很有时间整理. 今天主要是来吐槽下那些设计很复杂的ORM的. 项目做的越多,越觉得ORM这个东西设计的太复杂实在是没什么意义. 比较推崇Dapper这样比较简单,效率 ...

  7. THUWC2019爆零记

    Day -1 现在在机房里,准备敲敲板子什么的. 今天晚上放假诶,要好好睡一下.好好睡是不可能的,这辈子不可能的. Day 0 现在在酒店,\(lwh\)神仙在超越,我打了个\(treap\)的板子就 ...

  8. Appium+python自动化5-Appium Inspector

    前言    appium Inspector从入门到放弃!反正你都打开了,那就看下为什么要放弃吧! Appium Inspector是appium自带的一个元素定位工具,上一篇介绍了如何使用uiaut ...

  9. 关于ADO一个容易被忽视的问题!UpdateBatch [问题点数:0分]

    这是一个常见但容易被忽视的问题,旧贴有问及但没答案,因此提高分数.相信大家常这样使用Cache模式:ADOConnection1.BeginTrans;Try ADODataSet1.UpdateBa ...

随机推荐

  1. 消除a标签点击后产生的虚线框

    为a标签添加这条属性: a:focus {outline:none;-moz-outline:none;}

  2. ASI 与 AFN

    HTTP终结者.功能十分强大. 基于底层的CFNetwork框架,运行效率很高. 可惜

  3. Ceph剖析:定时器safetimer的实现

    定时器的作用是在指定的时间执行指定的动作.SafeTimer通过multimap数据结构维护定时项,定时项是时间和事件的Pair,定时项在map中按照定时时间从小到大排列.此外,SafeTimer使用 ...

  4. SDN三种模型解析

    数十年前,计算机科学家兼网络作家Andrew S. Tanenbaum讽刺标准过多难以选择,当然现在也是如此,比如软件定义网络模型的数量也很多.但是在考虑部署软件定义网络(SDN)或者试点之前,首先需 ...

  5. 来自MarsEdit的博客测试

    使用MarsEdit编辑的第一个测试博客. 希望我们一帆风顺! 插图,在插图时可以调整尺寸:   六种公式写法,记得要在选项中打开-启用数学公式: \begin{equation}\sum\end{e ...

  6. 关于Linux 下 Mysql 远程访问时出现的Access denied for user '用户名'@'IP地址' (using password:NO)

    大概是因为MySQL不允许远程访问时候不带密码吧,所以还是设定一个密码 如下这样做: 打开终端 ,即terminal的那个(RedHat5.x为例 在左上角(可能会移位)那个带着红帽的家伙点击,--- ...

  7. HttpWebRequest类

    HttpWebRequest类与HttpRequest类的区别. HttpRequest类的对象用于服务器端,获取客户端传来的请求的信息,包括HTTP报文传送过来的所有信息.而HttpWebReque ...

  8. angular之自定义过滤器的使用

    自定义过滤器需要使用filter函数,格式如下: filter("filterName',function(){ return function(target,args){ .... } } ...

  9. SVN服务器的配置(简单易懂,带配置文件,有注释)

    这两天在服务器搭建了一个SVN服务器,一些经验,也留作后用把,有不详细的欢迎批评指正 另外关于子目录的访问配置,这块我还是不懂,希望有前辈能教我一下 1.安装SVN Serveryum install ...

  10. Codeforces Round #347 (Div. 2) (练习)

    A: 题意:找到[a, b]的最大公约数: 思路:相同时为本身,不同时为1. 套路:碰到水题别想太多: 猜想:两个相邻数,必有一奇一偶,如果偶数有因子3或者其他,奇数可不可能有相同的呢? 枚举一些数后 ...