zoukankan      html  css  js  c++  java
  • C#如何重启一个计时器

    一. 废话

    今天在做项目的时候遇到了如何重启一个计时器的问题,C# 中有很多计时器,但是它们还真的没有一个用来  " Restart " 的方法。

    二. 没用的分类

    C# 系统中有好多种类的计时器:

    • System.Timers.Timer
    • System.Threading.Timer
    • System. Windows.Threading.DispatcherTimer
    • System.Windows.Forms.Timer

    三. 强行增加篇幅贴代码

    这边先使用 System.Timers.Timer 来做一下测试的代码演示。测试代码为:

    //10秒触发一次计时间隔
    const int Interval = 10 * 1000;
    
    //定义计时器
    Timer timer = new Timer(Interval);
    //计时器间隔触发事件
    timer.Elapsed += (o, e) =>
    {
        Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    };
    timer.Start();
    Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    //按下便是重置
    Console.ReadKey();
    Console.WriteLine("开始计时器重置");
    timer.Stop();
    timer.Interval = Interval;
    timer.Start();
    Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();
    Console.WriteLine("开始计时器暂停恢复能否重置?");
    timer.Stop();
    timer.Start();
    Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();
    Console.WriteLine("直接Start一次能否重置?");
    timer.Start();
    Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();

    运行结果:

    可以看到,我们可以使用两种方式来对 Timer 进行重置:

    1. 通过重新设置 Interval 的属性,然后再通过 Start 方法重新开启计时器(最后一种测试,只单纯使用 Start 方法开启计时器是没有效果的);

    这边通过观察 Timer 的 源代码 156 行,可以看到 Interval 的内部会做一个刷新的操作,如下图:

     

    2. 通过先 Stop 方法停止计时器,然后再使用 Start 方法进行重新开启;

    !!那么问题来了,是不是对于所有不同程序集下的计时器都是这样的呢?!!

     答:并不是的,不同计时器有点不同。下面是分别对 System.Threading.Timer 、System.Windows.Threading.DispatcherTimer 以及 System.Windows.Forms 的测试。

    ① 对 System.Threading.Timer 的测试:

    对于这个计时器,有一些坑可以在这篇文章中进行学习《 C# 工作总结(三):System.Threading.Timer 的回收问题 》。它并没有 Start 和 Stop 方法,也没有 Interval 属性,在构造函数中通过设置一些初始值之后,就会开始启动(有一个起始触发一次的延迟,可以看到每次启动之前先会步过这个事件先触发一次)。

    测试代码:

    //10秒触发一次计时间隔
    const int Interval = 10 * 1000;
    
    //定义计时器
    Timer timer = new Timer((state) =>
    {
        Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    },
    null, 0, Interval);
    Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    //按下便是重置
    Console.ReadKey();
    Console.WriteLine("开始计时器重置");
    timer.Change(0, Interval);
    Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();

    运行结果:

    可以看到我们只能通过使用 Change 的方法对这个计时器进行重置。

    ② 对 System.Windows.Threading.DispatcherTimer 的测试:

    这个计时器是来自于 WPF 框架的计时器,它和  System.Timers.Timer 和 System.Threading.Timer 的区别在于,它是在 UI 线程中运行的,有点类似于 System.Windows.Forms.Timer 。在 UI 线程中使用的区别就是,对于 System.Windows.Threading.DispatcherTimer 和 System.Windows.Forms.Timer 在修改 UI 的时候,不需要使用 Invoke 或者是 BeginInvoke 来避免跨线程访问修改 UI 控件的问题。

    经过测试发现,在 Console 中使用 System.Windows.Threading.DispatcherTimer 会失败(没有计时间隔触发,但也不抛出异常)。下面是一个测试:

    新建一个窗体的 Loaded 加载事件,然后输入如下代码:

    XAML 代码:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
    
        <ListBox x:Name="listBox" Grid.ColumnSpan="3" />
        <Button x:Name="btn1" Grid.Row="1" Grid.Column="0" Content="Interval重置" Click="btn1_Click"/>
        <Button x:Name="btn2" Grid.Row="1" Grid.Column="1" Content="Stop与Start重置" Click="btn2_Click"/>
        <Button x:Name="btn3" Grid.Row="1" Grid.Column="2" Content="仅仅Start" Click="btn3_Click"/>
    </Grid>

    后台代码:

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        //定义计时器
        private DispatcherTimer timer = null;
    
        //10秒触发一次计时间隔
        private const int Interval = 10;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //定义计时器
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(Interval);
            //计时器间隔触发事件
            timer.Tick += (_o, _e) =>
            {
                this.listBox.Items.Add("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            };
            timer.Start();
            this.listBox.Items.Add("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    
        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            //按下便是重置;
            this.listBox.Items.Add("开始计时器重置");
            timer.Stop();
            timer.Interval = TimeSpan.FromSeconds(Interval);
            timer.Start();
            this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    
        private void btn2_Click(object sender, RoutedEventArgs e)
        {
            this.listBox.Items.Add("开始计时器暂停恢复能否重置?");
            timer.Stop();
            timer.Start();
            this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    
        private void btn3_Click(object sender, RoutedEventArgs e)
        {
            this.listBox.Items.Add("直接Start一次能否重置?");
            timer.Start();
            this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    }

    运行结果:

    可以看出对于 System.Windows.Threading.DispatcherTimer 来说,它的重置方法和 System.Timers.Timer 一样。

    ③ 对 System.Windows.Forms.Timer 的测试:

    System.Windows.Forms.Timer 与 System.Windows.Threading.DispatcherTimer 的特点非常的类似,也是不能在 Console 控制台中运行。下面是测试代码:

    public partial class Form1 : Form
    {
        //定义计时器
        private Timer timer = null;
    
        //10秒触发一次计时间隔
        private const int Interval = 10 * 1000;
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            timer = new Timer();
            timer.Interval = Interval;
            timer.Tick += (_o, _e) =>
            {
                this.listBox1.Items.Add("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            };
            timer.Start();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Add("开始计时器重置");
            timer.Stop();
            timer.Interval = Interval;
            timer.Start();
            this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    
        private void button2_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Add("开始计时器暂停恢复能否重置?");
            timer.Stop();
            timer.Start();
            this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    
        private void button3_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Add("直接Start一次能否重置?");
            timer.Start();
            this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
    }

    运行结果:

    测试的结果和 System.Windows.Threading.DispatcherTimer 和 System.Timers.Timer 一致。

    四. 没啥用的技巧

     为使用方法,我们可以使用 扩展方法 对着几个 Timer 进行一下扩展,这边以 System.Timers.Timer 来做一下扩展。

    代码如下:

    /// <summary>
    /// 扩展方法类
    /// </summary>
    public static class Extensions
    {
        /// <summary>
        /// 给 System.Timers.Timer 增加一个扩展方法 Restart 用于重新开始重置计时间隔
        /// </summary>
        /// <param name="timer"></param>
        /// <param name="invertal">重置计时间隔的长度</param>
        public static void Restart(this System.Timers.Timer timer, int invertal = 0)
        {
            //重新开始
            timer.Stop();
    
            if (invertal > 0)
            {
                timer.Interval = invertal;
            }
            else
            {
                //set <- get
                timer.Interval = timer.Interval;//利用内部的change方法
            }
                
            //重新开始
            timer.Start();
        }
    }

    引入这个静态的扩展类之后,我们就可以上端使用这个扩展方法:

    //10秒触发一次计时间隔
    const int Interval = 10 * 1000;
    
    //定义计时器
    Timer timer = new Timer(Interval);
    //计时器间隔触发事件
    timer.Elapsed += (o, e) =>
    {
        Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    };
    timer.Start();
    Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();
    Console.WriteLine("开始计时器重置");
    
    //调用
    timer.Restart();
    Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
    Console.ReadKey();

    五. 失效的代码下载地址

    下载代码

  • 相关阅读:
    游戏中战斗伤害范围-弹道飞行
    游戏中战斗伤害范围攻击计算完整全版
    更加强健的线程模型,解决线程卡死,退出异常情况
    存在即合理,重复轮子orm java版本
    游戏里12方向,任意方向计算正前方矩形规则
    我是如何设计游戏服务器架构的
    游戏中精灵对象的属性功能设计
    看我是如何处理自定义线程模型---java
    面试和面试者如何保持心态
    谈谈枚举的新用法——java
  • 原文地址:https://www.cnblogs.com/Jeffrey-Chou/p/12361638.html
Copyright © 2011-2022 走看看