【问题标题】:Can the Elapsed callback of a System.Timers.Timer be async?System.Timers.Timer 的 Elapsed 回调可以是异步的吗?
【发布时间】:2015-03-26 11:09:39
【问题描述】:

是否有可能(甚至合理)将System.Timers.Timer 的回调设为异步方法?比如:

var timer = new System.Timers.Timer
{
   Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
   AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();

它编译(显然是一个很好的起点),但我不确定我是否理解后果。定时器await会不会在重置定时器前回调?

【问题讨论】:

    标签: c# async-await system.timers.timer


    【解决方案1】:

    定时器await会不会在重置定时器前回调?

    没有。它可以等待什么,因为ElapsedEventHandler 的签名具有 void 返回类型。

    换句话说,你的代码相当于:

    var timer = new System.Timers.Timer { ... };
    timer.Elapsed += Foo;
    timer.Start();
    
    ...
    private async void Foo()
    {
        ...
    }
    

    这是否适合您将取决于您的上下文。一般来说,拥有 async void 方法或匿名函数会使它们更难测试和重用 - 但正是为了事件处理程序而赋予了这种能力......不过,您应该考虑错误将如何传播。

    【讨论】:

      【解决方案2】:

      问题的标题是专门关于定时器的,但如果我们将其视为“如何在一段时间后调用异步方法?”那么你可以在不使用计时器的情况下做到这一点。

      var task2 = Task.Run(async () => {
          while (true)
          {
              try
              {
                  await MyMethod2();
              } catch
              {
                  //super easy error handling
              }
              await Task.Delay(TimeSpan.FromSeconds(5));
          }
      });
      
      ...
      
      public async Task MyMethod2()
      {
          //async work here
      }
      

      但请注意,这将有不同的时间(定时器将在一定时间间隔内调用,上面的代码将每隔(运行时间 + sleep_time)调用一次,但即使MyMethod2 需要很长时间它也不会被调用两次。话虽如此,您可以计算“每 x 分钟”运行等待多长时间。

      【讨论】:

      • 用它来减慢 Azure 上的网络请求。每秒超过 10 次会导致错误。谢谢。
      • 听起来不错。我将尝试在服务中对其进行测试。我通常使用计时器,但这次 OnElapsed 我必须进行几个连续的异步调用。它可能有效!非常感谢!
      【解决方案3】:

      其实可以的。

      System.Timers.Timer timer = new System.Timers.Timer();
      timer.Elapsed += async (x, y) => { await Task.Delay(1); };
      

      【讨论】:

        【解决方案4】:

        提出@tymtam 的解决方案不会等到MyMethod2 结束。
        我认为使用它会更好。
        一个包含两个异步任务的示例,当两个任务都完成后,等待 5 秒并再次执行这两个任务:

        var task2 = Task.Run(async () => {
            while (true)
            {
                try
                {
                    var task1 = MyMethod1();
                    var task2 = MyMethod2();
                    List<Task> allTasks = new List<Task> { task1, task2 };
                    while (allTasks.Count > 0)
                    {
                        Task finishedTask = await Task.WhenAny(allTasks);
                        if (finishedTask == task1)
                        {
                           Console.WriteLine("MyMethod1 has ended");
                        }
                        else if (finishedTask == task2)
                        {
                           Console.WriteLine("MyMethod2 has ended");
                        }
                        tareas.Remove(finishedTask);
                    }
                    //Here only when finished all task
                } catch
                {
                    //super easy error handling
                }
                //Wait until next cycle
                await Task.Delay(TimeSpan.FromSeconds(5));
            }
        });
        
        ...
        
        public async Task MyMethod1()
        {
            //async work here
        }
        
        public async Task MyMethod2()
        {
            //async work here
        }
        

        【讨论】:

        • tareas.Remove(finishedTask); tareas 变量未定义。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2019-11-05
        • 2020-04-27
        • 1970-01-01
        • 2011-04-10
        相关资源
        最近更新 更多