【问题标题】:c# Task cancellation when using System.Timersc# 使用 System.Timers 时取消任务
【发布时间】:2019-07-03 20:47:48
【问题描述】:

我不确定如何最好地取消正在运行系统计时器的任务。 在下面的代码中,每 60 分钟计时器将经过一次,然后运行另一个方法 (CheckFileOverflow),该方法用于检查系统日志 txt 的文件大小。文件

理想情况下,计时器的取消将通过单击按钮或调用取消的其他方法来完成。只要软件正在运行,计时器就可以有效地运行,但是当用户最终关闭软件时,我希望能够以负责任的方式取消任务,即不冒正在进行的线程池的风险在后台使用的资源挥之不去。

我花了很多时间阅读取消令牌,但仍然不明白:(

public void SystemEventLoggerTimer()
    {
        SysEvntLogFileChckTimerRun = true;

        Task.Run(() =>
        {
            System.Timers.Timer timer = new System.Timers.Timer
            { Interval = 1000 * 60 * 60 };
            timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
            timer.Start();
        });
    }

【问题讨论】:

  • SystemEventLoggerTimer() 什么时候被调用?

标签: c# cancellationtokensource system.timers.timer


【解决方案1】:

我建议您使用 Microsoft 的响应式框架(又名 Rx) - 只需 NuGet System.Reactive

然后你这样做:

IDisposable subscription =
    Observable
        .Interval(TimeSpan.FromHours(1.0))
        .Subscribe(_ => CheckFileOverflow());

如果您想取消订阅,只需致电subscription.Dispose()

Rx 非常适合抽象出计时器、事件、任务、异步操作等。

【讨论】:

    【解决方案2】:

    你可以把你的方法改成这样的

      public void SystemEventLoggerTimer(CancellationToken cancelToken)
            {
                SysEvntLogFileChckTimerRun = true;
    
                Task.Run(async () =>
                {
                    // Keep this task alive until it is cancelled
                    while (!cancelToken.IsCancellationRequested)
                    {
                        await Task.Delay(TimeSpan.FromMinutes(60));
                        CheckFileOverflow();
                    }
                });
            }
    

    然后你像这样调用 SystemEventLoggerTimer

    var cancelSource = new CancellationTokenSource();
    SystemEventLoggerTimer(cancelSource.Token);
    

    你可以在程序被释放时取消这个令牌,或者在你的主函数结束时取消这个令牌

    【讨论】:

      【解决方案3】:

      为什么不在调用上下文中(或在您的类/应用程序中全局访问)有一个可访问的计时器 - 无论如何,您必须使用 CancellationTokenSource 来做到这一点!这看起来不像是 Task 的正确用例。

      试试这个:

          public void SystemEventLoggerTimer(System.Timers.Timer timer)
          {
              SysEvntLogFileChckTimerRun = true;
      
              timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
              timer.Start();
          }
      

      调用代码:

          var timer = new System.Timers.Timer() { Interval = 1000 * 60 * 60 };
          SystemEventLoggerTimer(timer);
      

      取消代码(在取消按钮的事件处理程序等中):

          timer.Stop();
      

      【讨论】:

      • 您好,很遗憾,这种方法对我不起作用,似乎没有办法在计时器启动后停止它?
      • 我回答的最后一行:timer.Stop(); - 在单击事件上运行该代码,您想用来停止计时器的按钮(根据您的原始帖子)
      • 只需确保计时器对象在代码中的 Form1 中声明,而不是在任何方法中 - 这可能就是您无法访问它的原因。
      【解决方案4】:

      我在下面发布了似乎对我有用的令人满意的解决方案。希望我以正确的方式响应线程......(stackOverflow的新手) 我设置了一个用于测试的快速 Windows 表单,我创建了 2 个按钮和 1 个文本框。 按钮用于启动和停止计时器(使用取消令牌) 文本框用于监视计时器,该计时器将每 2 秒更新一次“计时器运行”消息。希望这可以帮助其他看到类似情况的人...

      enter image description here

      public partial class Form1 : Form
      {
          public Form1()
          {
              InitializeComponent();
          }
      
          private CancellationTokenSource cancelSource;
      
          // Button is used to START the timer.
          private void TimerStartButton_Click(object sender, EventArgs e)
          {
              cancelSource = new CancellationTokenSource();
              // Run the below method that will initiate timer to start running from 
              // the button click.
              SystemEventLoggerTimer(cancelSource.Token);
          }
      
          private void SystemEventLoggerTimer(CancellationToken cancelToken)
          {
              Task.Run(async () =>
              {
                  // Keep this task alive until it is cancelled
                  while (!cancelToken.IsCancellationRequested)
                  {
                      // Encapsulating the function Task.Delay with 'cancelToken'
                      // allows us to stop the Task.Delay during mid cycle.
                      // For testing purposes, have reduced the time interval to 2 secs.
                      await Task.Delay(TimeSpan.FromSeconds(2), cancelToken);
                      // Run the below method every 2 seconds.
                      CheckFileOverflow();
                  }
              });
          }
      
          // When the below method runs every 2 secs, the UpdateUI will allow
          // us to modify the textbox form controls from another thread.
          private void CheckFileOverflow()
          {
              UpdateTextbox("Timer Running");
          }
      
          // UpdateUI will allow us to modify the textbox form controls from another thread.
          private void UpdateTextbox(string s)
          {
              Func<int> del = delegate ()
              {
                  textBox1.AppendText(s + Environment.NewLine);
                  return 0;
              };
              Invoke(del);
          }
      
          // Button that is used to STOP the timer running.
          private void TimerStopButton_Click(object sender, EventArgs e)
          {
              // Initiate the cancelleation request to method "SystemEventLoggerTimer"
              cancelSource.Cancel();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-01-06
        • 1970-01-01
        • 2017-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多