一个简单的 Async/Await 示例:一个计时器

文章通过比较使用async/await和DispatcherTimer实现计时器的方式,探讨了两种方法的差异。在async/await中,代码更易于控制和理解,而基于事件的方法涉及事件处理和状态协调。两种方法都在处理计算耗时操作时调整延迟以保持每秒一次的迭代。
摘要由CSDN通过智能技术生成

我将从一个简单的例子开始:一个计时器。

private void StartButton_OnClick(object sender, RoutedEventArgs e)
{
    async Task Start()
    {
        int count = 1;
         
        while (true)
        {           
            this.Title = count + " iterations";
 
            count++;
 
     await Task.Delay(1000);
        }
    }
 
    Start();
}

此 StartButton_OnClick 方法是 WPF 应用程序中的事件处理程序。它在单击“开始”按钮时运行。

此方法定义了一个名为 Start 的本地函数,它以即发即弃的方式调用该函数。这个局部函数是异步的。运行此函数时,它基本上从 1 开始计数到无穷大。每一秒,它都会将计数器递增 1,并将结果显示在窗口的标题上。

几乎每一秒,窗口的标题都会改变。

使用 Task.Delay,我基本上能够创建一个每秒运行一次的计时器。

如果在每次迭代中我们进行了一些需要半秒的计算,那么计数器将每 1.5 秒更新一次。

private void StartButton_OnClick(object sender, RoutedEventArgs e)
{
    async Task Start()
    {
        int count = 1;
 
        while (true)
        {
            var calculationResult = RunCalculationThatTakesHalfASecond();
 
            this.Title = count + " iterations and result is :" + calculationResult;
 
            count++;
 
            await Task.Delay(1000);
        }
    }
 
    Start();
}

我们可以执行以下操作来尝试每秒递增计数器:

private void StartButton_OnClick(object sender, RoutedEventArgs e)
{
    async Task Start()
    {
        int count = 1;
 
        while (true)
        {
            var sw = Stopwatch.StartNew();
                     
            var calculationResult = RunCalculationThatTakesHalfASecond();
 
            this.Title = count + " iterations and result is :" + calculationResult;
 
            count++;
 
            var timeToWait = TimeSpan.FromSeconds(1) - sw.Elapsed;
 
            if (timeToWait > TimeSpan.Zero)
            {
                await Task.Delay(timeToWait);
            }
        }
    }
 
    Start();
}

在上面的代码中,我使用Stopwatch类测量处理每次迭代所花费的时间。处理后,我不会延迟下一次迭代整整一秒。相反,我将下一次迭代延迟一秒减去处理当前迭代所花费的时间。

除了 async/await,我还可以使用这样的计时器类:

private void StartButton_OnClick(object sender, RoutedEventArgs e)
{
    int count = 1;
 
    var timer = new System.Windows.Threading.DispatcherTimer();
 
    timer.Tick += (_, __) =>
    {
        var sw = Stopwatch.StartNew();
 
        var calculationResult = RunCalculationThatTakesHalfASecond();
 
        this.Title = count + " iterations and result is :" + calculationResult;
 
        count++;
 
        var timeToWait = TimeSpan.FromSeconds(1) - sw.Elapsed;
 
        timer.Interval = timeToWait > TimeSpan.Zero ? timeToWait : TimeSpan.Zero;
    };
     
    timer.Interval = TimeSpan.FromSeconds(1);
    timer.Start();
}

DispatcherTimer 类配置为每秒引发一次 Tick 事件。但它会在 Tick 事件处理程序完成后开始测量时间。因此,和以前一样,我使用 Stopwatch 来计算每次迭代所花费的时间,并调整下一次迭代的计时器等待间隔。

这两个示例之间的区别在于,在 async/await 的情况下,代码被建模为单个过程,而在计时器类的情况下,我们基本上有两个过程:启动计时器的过程 (StartButton_OnClick) 和事件处理程序每次新迭代开始时运行的 lambda。

将逻辑建模为单个过程使我们能够更好地控制并且更清晰。

例如,在 async/await 版本中,我使用 while 循环来对迭代进行建模。async/await 版本中的逻辑显然是使用过程代码建模的。

例如,比较我们如何在两个示例中指定每次迭代的等待时间。在 async/await 版本中,我们“等待”剩余时间并继续循环,而在定时器类版本中,我们设置了 Interval 属性。

在这个后来的版本中,就好像我们在两个不同的事物之间交流状态。lambda 中的代码告诉计时器对象在执行下一次迭代之前要等待多长时间。在 async/await 版本中,没有这样的通信。有一个单一的程序。

我将使用计时器类的方法称为基于事件的方法,因为我们编写代码来对事件做出反应。

在 async/await 版本中,我可以使用简单的 break 语句退出循环。在基于事件的版本中,我需要调用 timer.Stop() 这是两种事物之间的一种通信形式。

总而言之,async/await 版本允许我们对计时器逻辑进行建模,而基于事件的版本则需要我们进行协调。

在下一个示例中,这种差异将变得更加明显。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值