[CLK Framework] CLK.Threading.PortableTimer - 跨平台的Timer類別

摘要:[CLK Framework] CLK.Threading.PortableTimer - 跨平台的Timer類別

[CLK Framework] CLK.Threading.PortableTimer - 跨平台的Timer類別

問題情景

開發應用程式的時候,免不了需要加入一些定時執行的功能設計,例如說:定時更新畫面資料、定時檢查資料庫內容、定時檢查通訊是否斷線...等等。而.NET Framework也很貼心的提供三種不同的Timer類別,用來幫助開發人員設計出定時執行的功能模組。

.NET Framework提供的三種Timer類別,可以參考Bill叔的部落格:

但是當功能模組的開發,是以跨平台執行為目標來做設計的時候,因為不是每個平台都支援上列三種Timer,所以連帶的在跨平台的專案中,也就不支援參考使用.NET Framework所提供的Timer類別。像是下圖中所建立的Portable Class Library專案,就無法參考使用到System.Threading.Timer類別。

遇到這樣跨平台的功能模組開發,該如何提供跨平台的定時執行功能呢?

問題情景01

解決方案

處理跨平台的定時執行功能,其實解決方案很簡單,只要建立一個跨平台的Timer類別,用來提供定時執行的功能,就可以滿足這個設計需求。

而建立Timer類別最簡單的設計,就是開啟一條獨立的執行緒,透過這個執行緒定時去執行Callback函式,這就完成了Timer類別的功能設計。但是因為.NET Framework中所提供的System.Threading.Thread並不支援跨平台使用。所以執行緒的建立工作,必須改為可以跨平台使用的System.Threading.Tasks.Task來建立執行緒,這樣才能符合跨平台的開發需求。

使用跨平台的System.Threading.Tasks.Task類別來建立的執行緒,並且使用這個執行緒來定時執行Callback函式,這就完成了跨平台Timer類別的功能設計。

模組設計

CLK.Threading.PortableTimer是一個跨平台的Timer類別。在開發跨平台功能模組時,使用CLK.Threading.PortableTimer能夠幫助開發人員,簡化定時執行功能的開發工作。

模組下載

下載程式碼:由此進入GitHub後,點選右下角的「Download ZIP」按鈕下載。

模組程式碼:\CLK方案\01 Domains資料夾\CLK專案\Threading資料夾\PortableTimer類別

範例程式碼:\CLK方案\02 Samples資料夾\02 CLK.Threading.Samples資料夾\PortableTimer資料夾\

(開啟程式碼的時候,建議使用Visual Studio所提供的「大綱->摺疊至定義」功能來摺疊程式碼,以能得到較適合閱讀的排版樣式。)

物件程式

using CLK.Diagnostics;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace CLK.Threading
{
    public sealed class PortableTimer : IDisposable
    {
        // Fields
        private readonly ManualResetEvent _executeThreadEvent = new ManualResetEvent(false);

        private readonly Action _callback = null;

        private readonly int _interval = 0;


        // Constructors
        public PortableTimer(Action callback, int interval)
        {
            #region Contracts

            if (callback == null) throw new ArgumentNullException();

            #endregion

            // Require
            if (interval <= 0) throw new ArgumentException();

            // Arguments
            _callback = callback;
            _interval = interval;

            // Begin
            Task.Factory.StartNew(this.Execute);
        }

        public void Dispose()
        {
            // End
            _executeThreadEvent.Set();
        }


        // Methods
        private void Execute()
        {
            while (true)
            {
                // Wait
                if (_executeThreadEvent.WaitOne(_interval) == true)
                {
                    return;
                }

                // Execute
                try
                {
                    // Callback
                    _callback();
                }
                catch (Exception ex)
                {
                    // Fail
                    DebugContext.Current.Fail(string.Format("Action:{0}, State:{1}, Message:{2}", "Callback", "Exception", ex.Message));
                }
            }
        }
    }
}

使用範例

CLK.Threading.Samples.No001 - 在Windows Store App中使用PortableTimer

  • 使用範例

    using System;
    using System.Threading;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace CLK.Threading.Samples.No001
    {
        public sealed partial class MainPage : Page
        {
            // Fields
            private readonly object _syncRoot = new object();
    
            private readonly SynchronizationContext _syncContext = null;
    
            private PortableTimer _operateTimer = null;
    
    
            // Constructors
            public MainPage()
            {
                // Base
                this.InitializeComponent();
    
                // SyncContext
                _syncContext = SynchronizationContext.Current;
            }
    
    
            // Handlers
            private void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer != null) return;
    
                    // Begin
                    _operateTimer = new PortableTimer(this.Timer_Ticked, 500);
                }
            }
    
            private void MainPage_Unloaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer == null) return;
    
                    // End
                    _operateTimer.Dispose();
                    _operateTimer = null;
                }
            }
    
            private void Timer_Ticked()
            {
                System.Threading.SendOrPostCallback methodDelegate = delegate(object state)
                {
                    // Display            
                    this.TextBlock001.Text = DateTime.Now.ToString();
                };
                _syncContext.Post(methodDelegate, null);
            }
        }
    }
    
  • 執行結果

    使用範例01

CLK.Threading.Samples.No002 - 在Windows Phone App中使用PortableTimer

  • 使用範例

    using System;
    using System.Windows;
    using Microsoft.Phone.Controls;
    using System.Threading;
    
    namespace CLK.Threading.Samples.No002
    {
        public partial class MainPage : PhoneApplicationPage
        {
            // Fields
            private readonly object _syncRoot = new object();
    
            private readonly SynchronizationContext _syncContext = null;
    
            private PortableTimer _operateTimer = null;
    
    
            // Constructors
            public MainPage()
            {
                // Base
                this.InitializeComponent();
    
                // SyncContext
                _syncContext = SynchronizationContext.Current;
            }
    
    
            // Handlers
            private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer != null) return;
    
                    // Begin
                    _operateTimer = new PortableTimer(this.Timer_Ticked, 500);
                }
            }
    
            private void PhoneApplicationPage_Unloaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer == null) return;
    
                    // End
                    _operateTimer.Dispose();
                    _operateTimer = null;
                }
            }
    
            private void Timer_Ticked()
            {
                System.Threading.SendOrPostCallback methodDelegate = delegate(object state)
                {
                    // Display            
                    this.TextBlock001.Text = DateTime.Now.ToString();
                };
                _syncContext.Post(methodDelegate, null);
            }
        }
    }
    
  • 執行結果

    使用範例02

CLK.Threading.Samples.No003 - 在WPF中使用PortableTimer

  • 使用範例

    using System;
    using System.Threading;
    using System.Windows;
    
    namespace CLK.Threading.Samples.No003
    {
        public partial class MainWindow : Window
        {
            // Fields
            private readonly object _syncRoot = new object();
    
            private readonly SynchronizationContext _syncContext = null;
    
            private PortableTimer _operateTimer = null;
    
    
            // Constructors
            public MainWindow()
            {
                // Base
                this.InitializeComponent();
    
                // SyncContext
                _syncContext = SynchronizationContext.Current;
            }
    
    
            // Handlers
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer != null) return;
    
                    // Begin
                    _operateTimer = new PortableTimer(this.Timer_Ticked, 500);
                }
            }
    
            private void Window_Unloaded(object sender, RoutedEventArgs e)
            {
                lock (_syncRoot)
                {
                    // Require
                    if (_operateTimer == null) return;
    
                    // End
                    _operateTimer.Dispose();
                    _operateTimer = null;
                }
            }
    
            private void Timer_Ticked()
            {
                System.Threading.SendOrPostCallback methodDelegate = delegate(object state)
                {
                    // Display            
                    this.TextBlock001.Text = DateTime.Now.ToString();
                };
                _syncContext.Post(methodDelegate, null);
            }
        }
    }
    
  • 執行結果

    使用範例03

期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。