【问题标题】:Running a task periodically with a cancellation token called from another method使用从另一个方法调用的取消令牌定期运行任务
【发布时间】:2017-09-20 20:58:43
【问题描述】:

我试图每隔 x 分钟运行一次屏幕截图任务,但似乎当我想使用给定的取消令牌取消该任务时,它似乎没有做任何事情。

这是我的启动方法代码:

var CancellationTokenSource = new CancellationTokenSource();

        CancellationTokenSource?.Cancel();

        CancellationTokenSource = new CancellationTokenSource();

        var token = CancellationTokenSource.Token;
        await RunPeriodically(async () =>
        {

            var screenCaptured = TakeScreenshot();

            if (screenCaptured == null || CancellationTokenSource.Token.IsCancellationRequested)
                return;

            var correctUserName = Settings.Default.Username.Split('.');
            var parsedUsername = correctUserName[0] + " " + correctUserName[1];
            await ScreenshotHelper.UploadScreenshotAsync(ProjectName, "screenshotscontainer",
                screenCaptured.ToArray(), Environment.MachineName, parsedUsername);
            Console.WriteLine("Took Screenshot: " + DateTime.Now.ToString(CultureInfo.InvariantCulture));

        }, TimeSpan.FromSeconds(3), token);

这是定期运行的代码:

public async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
    {
        while (true)
        {
            action();
            await Task.Delay(interval, token);
        }
    }

【问题讨论】:

  • 您在 RunPeriodically 方法中调用了没有 awaitasync Action。而async void 通常是一个糟糕的决定。重新设计。
  • 我不明白为什么它没有取消,但我看到了一些其他问题:请注意,当您 await 上传时,action() 调用返回。所以它可能比你想象的更频繁地调用。而while(true) 不会处理CancellationToken(尽管Task.Delay 应该抛出TaskCancelledException afaik)。
  • 您实际上是在什么时候取消任务?您创建了一个新的CancellationTokenSource,并且在此之后永远不会取消它。
  • 我还建议使用 System.Threading.Timer。这将从线程池中拉出一个线程并避免阻塞线程。

标签: c# wpf xaml asynchronous async-await


【解决方案1】:

我想我应该举一个类的快速示例,该类将运行一个进程,等待一段时间并再次启动该进程而不会阻塞任何线程。

class RepeatableProcess
{
    private Timer  processTimer;
    private int delay;
    private CancellationTokenSource source;
    private CancellationToken token;
    private Action processToRun;
    private bool canStart = true;


    public RepeatableProcess(int delaySeconds,Action process)
    {
        delay = delaySeconds;
        processToRun = process;
    }

    public void Start()
    {
        if (canStart)
        {
            canStart = false;
            source = new CancellationTokenSource();
            token = source.Token;
            processTimer = new Timer(TimedProcess, token, Timeout.Infinite, Timeout.Infinite);
            processTimer.Change(0, Timeout.Infinite);
        }

    }

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

    public void TimedProcess(object state)
    {

        CancellationToken ct = (CancellationToken)state;
        if (ct.IsCancellationRequested)
        {
            Console.WriteLine("Timer Stopped");
            processTimer.Dispose();
            canStart = true;
        }
        else
        {
            processToRun.Invoke();
            processTimer.Change(delay, Timeout.Infinite);
        }
    }

}

Start 方法创建一个永不启动且永不重复的计时器。 然后它立即启动进程,只运行一次。

TimedProcess 方法检查取消并运行指定的进程。进程完成后,计时器设置为在指定延迟后启动,并且只运行一次。

当计时器触发时,它会从线程池中获取一个线程。不会出现超限问题,因为在进程完成之前不会将计时器设置为再次运行。

这个类需要更多的保护,但这只是一个例子。 希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2022-12-23
    • 1970-01-01
    • 2022-07-11
    • 1970-01-01
    相关资源
    最近更新 更多