【问题标题】:Is this the correct way to run scheduled async/await methods using System.Threading.Timer?这是使用 System.Threading.Timer 运行预定的异步/等待方法的正确方法吗?
【发布时间】:2015-10-14 19:17:52
【问题描述】:

我目前正在从事一个项目,我想在后台执行一些定期更新,并且我想通过 System.Threading.Timer 使用 async/await 来实现此目的。我找不到任何关于这个特定主题的文章。

以下 sn-p 有效。我只是不确定使用返回 void 的异步方法应该只用于事件处理程序,如按钮单击。下面的代码中是否存在“违反”最佳实践的内容?

public class ScheduledCache
{
    private CancellationTokenSource _cancelSource = new CancellationTokenSource();
    private Request _request = new Request();
    private Timer _timer;

    public void Start()
    {
        _cancelSource = new CancellationTokenSource();
        _timer = new Timer(UpdateAsync, null, 2000, Timeout.Infinite);
    }

    public void Stop()
    {
        _cancelSource.Cancel();
    }

    public async void UpdateAsync(object state)
    {
        try
        {
            await Task.WhenAll(UpdateSomethingAsync(_cancelSource.Token), UpdateSomethingElseAsync(_cancelSource.Token));
        }
        catch (OperationCanceledException)
        {
            // Handle cancellation
        }
        catch (Exception exception)
        {
            // Handle exception
        }
        finally
        {
            if (_cancelSource.IsCancellationRequested)
                _timer = new Timer(UpdateAsync, null, 2000, Timeout.Infinite);
            else
                _timer = new Timer(UpdateAsync, null, Timeout.Infinite, Timeout.Infinite);
        }
    }

    private async Task UpdateSomethingAsync(CancellationToken cancellationToken)
    {
        await Task.Run(new Action(_request.UpdateSomething));
    }

    private async Task UpdateSomethingElseAsync(CancellationToken cancellationToken)
    {
        await Task.Run(new Action(_request.UpdateSomethingElse));
    }
}

public class Request
{
    public void UpdateSomething()
    {
        // Do some updates here
    }

    public void UpdateSomethingElse()
    {
        // Do some other updates here
    }
}

【问题讨论】:

    标签: c# multithreading asynchronous timer async-await


    【解决方案1】:

    我只是不确定是否应该使用返回 void 的异步方法,而该方法只能用于按钮点击等事件处理程序

    好吧,您正在向Timer.Elapsed 事件注册一个事件处理程序,所以以这种方式使用它是可以的。

    一般来说,我会做一些不同的事情。首先,我会避免使用async over sync anti-pattern。将Task.Run 移动到调用堆栈中可能的最高位置。然后,如果您只调用单线异步方法,只需返回它而不是等待它,这样您就可以保存异步状态机生成。

    您可能会考虑的另一件事是,您可以循环使用Task.Delay,而不是使用Timer,它在内部使用计时器,但向调用者公开Task

    大概是这样的:

    public async Task StartAsync()
    {
        _cancelSource = new CancellationTokenSource();
        await UpdateAsync(_cancelSource.Token);
    }
    
    public async Task UpdateAsync(CancellationToken cancellationToken)
    {
        try
        {
             var updateSomething = Task.Run(() => _request.UpdateSomething()));
             var updateSomethingElse = Task.Run(() => _request.UpdateSomethingElse());
    
            await Task.WhenAll(updateSomething, updateSomethingElse);
        }
        catch (OperationCanceledException)
        {
            // Handle cancellation
        }
        catch (Exception exception)
        {
            // Handle exception
        }
        finally
        {
            if (_cancelSource.IsCancellationRequested)
                await Task.Delay(2000);
        }
    }
    

    【讨论】:

    • 谢谢。很好的输入。是的,我不确定如何调用该方法。我完全同意将 Task.Run 移动到尽可能高的位置。更干净的代码。我现在确实看到了反模式。对于 async/await 的新手来说并不总是那么明显。
    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 2014-12-17
    • 2020-08-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-06
    • 1970-01-01
    相关资源
    最近更新 更多