这是使用System.Threading.Timer运行预定的async/await方法的正确方法吗?

PKV*_*PKV 3 c# multithreading asynchronous timer async-await

我目前正在开发一个项目,我想在后台执行一些定期更新,我想使用System.Threading.Timer为此目的使用async/await.我找不到关于这个特定主题的任何文章.

以下代码段有效.我只是不确定使用异步方法返回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
    }
}
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 5

我只是不确定使用异步方法返回void,因为它只应用于按钮点击等事件处理程序

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

一般来说,我会做一些不同的事情.首先,使用异步过同步反模式,我会避免.移动Task.Run到callstack中可能的最高位置.然后,如果您只是调用单线程异步方法,只需返回它而不是等待它,允许您保存异步状态机生成.

你可以考虑的另一件事是代替使用Timer你可以循环和使用Task.Delay,它在内部使用一个计时器,但暴露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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.好的投入.是的,我不确定如何调用该方法.我完全同意尽可能高地移动Task.Run.更清洁的代码.我现在确实看到了反模式.对于新手来说,异步/等待并不总是显而易见的. (2认同)