C# 定时器改进版

 

一、概述

前不久写了一篇名为 “C# 定时器封装版” 的帖子,它是用的定时器 + 事件订阅 的方式完成的,虽然可以实现需求,但是它有个缺点,就是定时器的执行的间隔时间只能用固定的时间,假设你想每个事件有自己的单独间隔时间那是不行,于是后面我在想如何解决这个问题,让加入的每一个委托都能设置自己的间隔时间,劈里啪啦乱写一通后,终于实现了,虽然写的不是特别好,但还是可以用的。

最近写了很多篇关于定时器帖子,包括一篇 C# 模拟 Unity3d 协程的帖子,也是为了解决大型项目中的定时器混乱问题,那么定时器使用过多会造成那些危害呢?

定时器会造成:

1.关闭程序之前,假设不关闭定时器,有时候程序都关闭不了,一直处于卡死的状态。

2.程序运行的时间长了,很容易闪退。

3.定时器没有统一管理,比较混乱,时间久了,自己都不记得用了几个定时器了。

如果用一个定时器,解决所有的需求,不更符合设计模式的“单一职责原则” 。

二、实现功能

新建一个 winform 项目,给界面随便添加几个按钮,如下:

C# 定时器改进版_第1张图片

 添加一个类 TimerInfo

using System;

internal class TimerInfo
{
    /// 
    /// 定时器的名字(关闭定时器用)
    /// 
    public string TimerName { get; set; }
    /// 
    /// 定时器委托
    /// 
    public Action Tick { get; set; }
    /// 
    /// 间隔时间(毫秒)
    /// 
    public int Interval { get; set; }
    /// 
    /// 开始执行的时间
    /// 
    public DateTime StartTimer { get; set; }
}

TimerInfo 的作用相当于一个任务的结构体,任务的名字,委托,和间隔时间需要赋值,StartTimer 则是当前任务倒计时的开始时间,这里不用赋值。

添加一个类 TimeInterval

using System;

/// 
/// 时间差计算
/// 
public class TimeInterval
{
    /// 
    /// 计算两个时间间隔的时长
    /// 
    /// 返回的时间类型
    /// 开始时间
    /// 结束时间
    /// 返回间隔时间,间隔的时间类型根据参数 TimeType 区分
    public static double GetSpanTime(TimeType TimeType, DateTime StartTime, DateTime EndTime)
    {
        TimeSpan ts1 = new TimeSpan(StartTime.Ticks);
        TimeSpan ts2 = new TimeSpan(EndTime.Ticks);
        TimeSpan ts = ts1.Subtract(ts2).Duration();
        //TimeSpan ts = EndTime - StartTime;

        double result = 0f;
        switch (TimeType)
        {
            case TimeType.MilliSecond:
                result = ts.TotalMilliseconds;
                break;
            case TimeType.Seconds:
                result = ts.TotalSeconds;
                break;
            case TimeType.Minutes:
                result = ts.TotalMinutes;
                break;
            case TimeType.Hours:
                result = ts.TotalHours;
                break;
            case TimeType.Days:
                result = ts.TotalDays;
                break;
        }
        return result;
    }
    
    private TimeInterval() { }
}

/// 
/// 时间类型
/// 
public enum TimeType
{
    /// 
    /// 毫秒
    /// 
    MilliSecond,
    /// 
    /// 秒
    /// 
    Seconds,
    /// 
    /// 分钟
    /// 
    Minutes,
    /// 
    /// 小时
    /// 
    Hours,
    /// 
    /// 天
    /// 
    Days,
    /// 
    /// 月
    /// 
    Months
}

TimeInterval 类的主要作用是判断两个时间间隔了多久。

添加一个类 TimerOptimize

using System;
using System.Collections.Generic;
using System.Linq;

internal class TimerOptimize
{
    private static List TimerList = new List();

    private static List RemoveList = new List();

    private static System.Timers.Timer Timer1 = null;


    private static void Init()
    {
        Timer1 = new System.Timers.Timer();
        Timer1.Interval = 200;
        Timer1.AutoReset = true;
        Timer1.Elapsed += Timer1_Elapsed;
    }

    /// 
    /// 添加定时器
    /// 
    /// 
    public static void Add(TimerInfo timer)
    {
        if (!Check(timer)) return;

        timer.StartTimer = DateTime.Now;
        TimerList.Add(timer);

        if (Timer1 != null && !Timer1.Enabled)
            Timer1.Enabled = true;
    }

    /// 
    /// 移除定时器
    /// 
    /// 
    public static void Remove(string timerName)
    {
        if (string.IsNullOrEmpty(timerName)) return;
        if (RemoveList.Contains(timerName)) return;
        var collect = TimerList.Where(p => p.TimerName == timerName).ToList();
        if (collect == null || collect.Count == 0) return;

        RemoveList.Add(timerName);
    }


    private static void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        if (TimerList.Count > 0)
        {
            foreach (var item in TimerList)
            {
                double timer = TimeInterval.GetSpanTime(TimeType.MilliSecond, item.StartTimer, DateTime.Now);
                if (timer > item.Interval)
                {
                    item.StartTimer = DateTime.Now;
                    if (item.Tick != null)
                        item.Tick();
                }
            }
        }
        if (RemoveList.Count > 0)
        {
            List removeIndexList = new List();
            for (int i = 0; i < RemoveList.Count; i++)
            {
                int index = TimerList.FindIndex(p => p.TimerName == RemoveList[i]);
                if (index >= 0)
                {
                    TimerList.RemoveAt(index);
                    removeIndexList.Add(i);
                    Console.WriteLine("[TimerOptimize]移除定时器 {0}", RemoveList[i]);
                }
            }
            if (removeIndexList.Count > 0)
            {
                for (int i = 0; i < removeIndexList.Count; i++)
                {
                    RemoveList.RemoveAt(removeIndexList[i]);
                }
            }
            if (Timer1 != null && TimerList.Count == 0)
            {
                Timer1.Enabled = false;
                Console.WriteLine("[TimerOptimize]定时器已经关闭");
            }
        }
    }

    private static bool Check(TimerInfo timer)
    {
        if (timer == null)
        {
            Console.WriteLine("[TimerOptimize]timer 不能为空");
            return false;
        }
        if (timer.Tick == null)
        {
            Console.WriteLine("[TimerOptimize]timer.Tick 不能为空");
            return false;
        }
        if (string.IsNullOrEmpty(timer.TimerName))
        {
            Console.WriteLine("[TimerOptimize]timer.TimerName 不能为空");
            return false;
        }
        if(timer.Interval < 200)
        {
            Console.WriteLine("[TimerOptimize]timer.Interval 间隔时间太短");
            return false;
        }
        if (TimerList.Any(p => p.TimerName == timer.TimerName))
        {
            Console.WriteLine("[TimerOptimize]不能重复的添加,TimerName:{0}", timer.TimerName);
            return false;
        }
        return true;
    }

    static TimerOptimize()
    {
        Init();
    }

    private TimerOptimize()
    {
    }

    ~TimerOptimize()
    {
        Timer1.Enabled = false;
    }
}

TimerOptimize 类的主要作用是存储这些任务,判断什么时候执行回调,什么时候移除任务等。

主要代码都在这里了,下面开始测试。

三、测试

Form1 代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 定时器
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
        }

        private void button1_Click(object sender, EventArgs e)
        {
            TimerOptimize.Add(new TimerInfo()
            {
                TimerName = "Timer1",
                Tick = Test1,
                Interval = 1000
            });
            Console.WriteLine("添加 timer1");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            TimerOptimize.Add(new TimerInfo()
            {
                TimerName = "Timer2",
                Tick = Test2,
                Interval = 2000
            });
            Console.WriteLine("添加 timer2");
        }

        private void button3_Click(object sender, EventArgs e)
        {
            TimerOptimize.Add(new TimerInfo()
            {
                TimerName = "Timer3",
                Tick = Test3,
                Interval = 3000
            });
            Console.WriteLine("添加 timer3");
        }

        private void button4_Click(object sender, EventArgs e)
        {
            TimerOptimize.Remove("Timer1");
        }

        private void button5_Click(object sender, EventArgs e)
        {
            TimerOptimize.Remove("Timer2");
        }

        private void button6_Click(object sender, EventArgs e)
        {
            TimerOptimize.Remove("Timer3");
        }

        private void Test1()
        {
            Console.WriteLine("定时器1");
        }

        private void Test2()
        {
            Console.WriteLine("定时器2");
        }

        private void Test3()
        {
            Console.WriteLine("定时器3");
        }
    }
}

执行定时器1,并取消任务

C# 定时器改进版_第2张图片

执行定时器2,3,并取消任务

C# 定时器改进版_第3张图片

如果 TimerOptimize 类的 TimerList 为空时,会自动关闭定时器,这样能有效的节约系统资源,我粗略的看了下倒计时的时间,差不多是准确的,那么功能也就实现了,如果有需要改进的地方,欢迎留言告诉我,谢谢!

结束

如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言

end

你可能感兴趣的:(C#,c#)