C# 定时器改进版

news2024/11/18 16:31:15

 

一、概述

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

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

定时器会造成:

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

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

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

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

二、实现功能

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

 添加一个类 TimerInfo

using System;

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

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

添加一个类 TimeInterval

using System;

/// <summary>
/// 时间差计算
/// </summary>
public class TimeInterval
{
    /// <summary>
    /// 计算两个时间间隔的时长
    /// </summary>
    /// <param name="TimeType">返回的时间类型</param>
    /// <param name="StartTime">开始时间</param>
    /// <param name="EndTime">结束时间</param>
    /// <returns>返回间隔时间,间隔的时间类型根据参数 TimeType 区分</returns>
    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() { }
}

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

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

添加一个类 TimerOptimize

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

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

    private static List<string> RemoveList = new List<string>();

    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;
    }

    /// <summary>
    /// 添加定时器
    /// </summary>
    /// <param name="timer"></param>
    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;
    }

    /// <summary>
    /// 移除定时器
    /// </summary>
    /// <param name="timerName"></param>
    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<int> removeIndexList = new List<int>();
            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,并取消任务

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

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

结束

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

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/816247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HEVC 速率控制(码控)介绍

视频编码速率控制 速率控制&#xff1a; 通过选择一系列编码参数&#xff0c;使得视频编码后的比特率满足所有需要的速率限制&#xff0c;并且使得编码失真尽量小。速率控制属于率失真优化的范畴&#xff0c;速率控制算法的重点是确定与速率相关的量化参数&#xff08;Quantiz…

医学影像PACS系统源码:多功能服务器和阅片系统

PACS系统是以最新的IT技术为基础&#xff0c;遵循医疗卫生行业IHE/DICOM3.0和HL7标准&#xff0c;开发的多功能服务器和阅片系统。通过简单高性能的阅片功能&#xff0c;支持繁忙时的影像诊断业务&#xff0c;拥有保存影像的院内Web传输及离线影像等功能&#xff0c;同时具有备…

Python中的列表怎么排序

目录 Python中的列表是什么 python怎么给列表排序 给列表排序需要注意什么 总结 Python中的列表是什么 在Python中&#xff0c;列表&#xff08;List&#xff09;是一种有序且可变的数据类型。它允许存储多个元素&#xff0c;并且可以根据需要进行修改。 列表使用方括号&…

自定义类型讲解

&#x1f495;痛苦难道是白忍受的吗&#xff1f;&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;自定义类型讲解 一.结构体 定义&#xff1a; 数组&#xff1a;多组相同类型元素的集合 结构体&#xff1a;多组不同类型元素的集合-->管理多组不同类型数据…

大家做性能测试都用什么工具

在进行测试时&#xff0c;选择适合的测试工具至关重要&#xff0c;因为优秀的测试工具能够显著提高工作效率。对于性能测试和自动化测试而言&#xff0c;大多数人会选择传统的JMeter等工具&#xff0c;然而这些工具存在学习成本高、使用门槛高的问题。 因此&#xff0c;我在这…

微信小程序开发学习之--地图绘制行政区域图

不知道大家有没有感觉就是在做微信小程序地图功能时刚刚接触时候真的感觉好迷茫呀&#xff0c;文档看不懂&#xff0c;资料找不到&#xff0c;就很难受呀&#xff0c;比如我现在的功能就想想绘制出一个区域的轮廓图&#xff0c;主要是为了显眼&#xff0c;效果图如下&#xff1…

官方Office 技巧免费学习平台-WPS学堂

WPS学堂是WPS官方Office 技巧免费学习平台&#xff0c;目前网站累计上线 3000个免费教学视频图文&#xff0c;包含WPS表格(Excel)、WPS文字&#xff08;Word&#xff09;、WPS演示(PPT)的操作技巧及新手入门系列课视频&#xff0c;而且教学视频都可以直接在线学习&#xff0c;不…

14.2 【Linux】软件磁盘阵列(Software RAID)

14.2.1 什么是 RAID 磁盘阵列全名是“ Redundant Arrays of Inexpensive Disks, RAID ”&#xff0c;英翻中的意思是&#xff1a;容错式廉价磁盘阵列。 RAID 可以通过一个技术&#xff08;软件或硬件&#xff09;&#xff0c;将多个较小的磁盘整合成为一个较大的磁盘设备&…

图数据库Neo4j学习四——Spring Data NEO

1配置 1.1Maven依赖 <!--neo4j --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency>1.2yml配置 spring:data:neo4j:uri: bolt://localhost:76…

【机器学习】Cost Function for Logistic Regression

Cost Function for Logistic Regression 1. 平方差能否用于逻辑回归&#xff1f;2. 逻辑损失函数loss3. 损失函数cost附录 导入所需的库 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_logistic_loss import plt_logistic_cost, plt_two_…

利用易查分制作分班查询系统,怎么导入数据?

暑假过半&#xff0c;新学期即将到来&#xff0c;这对学校来说是一个重要的时刻。新学期的开始意味着学校将面临新生入学和老生升入高年级的情况&#xff0c;这就需要进行分班工作的安排。分班工作是一项繁琐而关键的任务&#xff0c;它直接关系到学生们在新学期中的班级和同学…

【Linux进程篇】进程概念(1)

【Linux进程篇】进程概念&#xff08;1&#xff09; 目录 【Linux进程篇】进程概念&#xff08;1&#xff09;进程基本概念描述进程-PCBtask_struct-PCB的一种task_ struct内容分类 组织进程查看进程通过系统调用获取进程标示符通过系统调用创建进程——fork初识 作者&#xff…

SpringMVC源码分析 —— 拦截器是何时调用的

SpringMVC源码分析&#xff0c;拦截器是何时、以什么方式调用的&#xff1f;本文将进行详细说明 环境准备 springboot 2.3.7.RELEASE 笔者创建一个springboot的web项目&#xff0c;使用的springboot的版本是2.3.7.RELEASE 对应的spring-web版本是5.2.12.RELEASE 下面将对上面…

【NLP-新工具】语音转文本与OpenAI的用途

一、说明 OpenAI最近2022发布了一个名为Whisper的新语音识别模型。与DALLE-2和GPT-3不同&#xff0c;Whisper是一个免费的开源模型。它的主要功能就是将语音翻译成文本。本文将介绍如何使用这个重要应用库。 二、 Whisper概念 2.1 Whisper是啥&#xff1f; Whisper 是一种自动…

考完软考,有什么备考心得和学习经验可以分享吗?

恭&#xfffd;&#xfffd;您完成软考考试&#xff01;备考软考确实是一项艰苦的任务&#xff0c;但也是一次很有收获的学习和成长过程。下面分享一些备考心得和学习经验&#xff0c;以及针对系统集成项目管理工程师和信息系统项目管理师考试的备考建议&#xff1a; 备考心得…

PACS系统源码:支持三维重建功能、集成放射科管理RIS系统、图文报告编辑、打印、多级审核机制

PACS系统源码 PACS系统是以最新的IT技术为基础&#xff0c;遵循医疗卫生行业IHE/DICOM3.0和HL7标准&#xff0c;开发的多功能服务器和阅片系统。通过简单高性能的阅片功能&#xff0c;支持繁忙时的影像诊断业务&#xff0c;拥有保存影像的院内Web传输及离线影像等功能&#xf…

【Android】APP网络优化学习笔记

网络优化原因 进行网络优化对于移动应用程序而言非常重要&#xff0c;原因如下&#xff1a; 用户体验&#xff1a; 网络连接是移动应用程序的核心功能之一。通过进行网络优化&#xff0c;可以提高应用的加载速度和响应速度&#xff0c;减少用户等待时间&#xff0c;提供更流…

跨境电商还有人在做吗,这十大选品技巧建议收藏!

随着电商的快速发展&#xff0c;无论国内或者国外电商&#xff0c;竞争都比较激烈&#xff0c;很多人觉得现在入行太晚了&#xff0c;玩不过那些老卖家。 不过我想说的是&#xff1a;做电商很重要的一点就是选品&#xff0c;那些很早一批老卖家可能也是借着红利期走过来的&…

ATFX汇市:美联储加息25基点,虽提振美指,或招致衰退

环球汇市行情摘要—— 上周&#xff0c;美元指数上涨0.6%&#xff0c;收盘在101.7点&#xff0c; 欧元贬值0.96%&#xff0c;收盘价1.1017点&#xff1b; 日元升值0.47%&#xff0c;收盘价141.16点&#xff1b; 英镑贬值0.01%&#xff0c;收盘价1.2852点&#xff1b; 瑞郎…

微信小程序配置上传多个u-upload上传

微信小程序配置上传多个u-upload上传 使用的是uView框架 微信小程序配置上传多个u-upload上传图片 场景需求&#xff1a;根据PC端配置项追加图片配置 小程序根据配置的图片数量&#xff0c;图片名称&#xff0c;进行上传图片 难度在于 我们不知道用户会追加多少个图片配置字段 …