2021-10-16 12:50

週期性執行的子執行緒範本

關於週期性執行的子執行緒有幾個要點:

  • 單次 Sleep 的時間不要太久,會影響程式的關閉
  • 不可以用 SpinWait 來做程式暫停,要用 Sleep 來暫停,這樣才會釋放 CPU
  • Error log 要注意會重複出現相同的 error
  • 有排除重複 error 時,要記得加上[錯誤解除]的 log,這樣才能知道程式是否有回到正常執行
  • 要執行的程式邏輯移到一個 method 裡,這樣可以避免 return 造成的迴圈中斷,也能分離邏輯的關注點


週期小於 10 秒的範本

private static readonly ILogger _log = LogManager.GetCurrentClassLogger();

private int _cycleMSec = 1000;
private bool _runFlag = false;
private string _prevError;

public void Start()
{
    if (_runFlag) { return; }
    _runFlag = true;

    var thread = new Thread(() =>
    {
        while (_runFlag)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(_cycleMSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }
    });

    thread.Start();
}

public void Stop()
{
    _runFlag = false;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}


週期大於 10 秒的範本

private static readonly ILogger _log = LogManager.GetCurrentClassLogger();

private int _cycleSec = 30;
private bool _runFlag = false;
private string _prevError;
private DateTime _nextTime = DateTime.Now;

public void Start()
{
    if (_runFlag) { return; }
    _runFlag = true;

    var thread = new Thread(() =>
    {
        while (_runFlag)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(1000);

            /* 檢查是否符合執行時間 */
            if (_nextTime > DateTime.Now) { continue; }

            /* 更新下一次的執行時間 */
            _nextTime = DateTime.Now.AddSeconds(_cycleSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }
    });

    thread.Start();
}

public void Stop()
{
    _runFlag = false;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}


停啟頻繁的範本

public enum CycleStatus
{
    Stop,
    Start,
    Stoping,
}

public CycleStatus RunStatus { get; private set; } = CycleStatus.Stop;


private static readonly ILogger _log = LogManager.GetCurrentClassLogger();
private int _cycleMSec = 1000;
private string _prevError;


public void Start()
{
    if (RunStatus != CycleStatus.Stop)
    { throw new Exception("程序還在進行中"); }

    RunStatus = CycleStatus.Start;

    var thread = new Thread(() =>
    {
        while (RunStatus == CycleStatus.Start)
        {
            /* 先睡可以避免在程式啟動時過於忙碌 */
            Thread.Sleep(_cycleMSec);

            try
            {
                /* 要執行的程式邏輯 */
                cycleHandler();
                if (_prevError != null) { _log.Info("錯誤結束"); }
                _prevError = null;
            }
            catch (Exception ex)
            {
                /* 避免相同的錯誤一直被記錄 */
                if (_prevError == ex.Message) { continue; }

                _prevError = ex.Message;
                _log.Fatal(ex, "執行錯誤");
            }
        }

        RunStatus = CycleStatus.Stop;
    });

    thread.Start();
}


public void Stop()
{
    if (RunStatus == CycleStatus.Stop)
    { throw new Exception("程序已經停止"); }

    if (RunStatus == CycleStatus.Stoping)
    { throw new Exception("程序正在停止"); }

    RunStatus = CycleStatus.Stoping;
}


private void cycleHandler()
{
    // 主要的邏輯程式寫在這裡
}

0 回應: