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();

五. 失效的代碼下載地址

下載代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM