如何取消和重新启动 C# 任务 [英] How to cancel and restart a C# Task

查看:68
本文介绍了如何取消和重新启动 C# 任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个长时间运行、长时间间隔的轮询过程.我需要能够强制更新并重新启动轮询.

I have a long running, long interval, polling process. I need to be able to force an update and restart the polling.

想到的最明显的事情是取消轮询任务,并开始一个新的,因为初始循环总是更新.

The most obvious thing that came to mind was to cancel the polling task, and start a new one since the initial loop always updates.

我试图找出最好的方法来做到这一点,使用 OperationCanceledException 来控制程序流对我来说似乎很奇怪 - 但也许这是正确的选择?这就是我目前所拥有的:

I'm trying to figure out the best way to do this, using OperationCanceledException to control program flow seems weird to me - but perhaps it's the right choice? This is what I have at the moment:

 public void Start()
 {
     var previousDateTime = DateTime.MinValue;

     CancellationTokenSource = new CancellationTokenSource();
     CancellationToken = CancellationTokenSource.Token;

     ASMTask = Task.Run(async () =>
     {
         try
         {
             while (!CancellationToken.IsCancellationRequested)
             {
                 if (CheckForUpdate())
                 {
                     Update(previousDateTime);
                 }

                 await Task.Delay(PollingInterval * 1000, CancellationToken);
             }
             CancellationToken.ThrowIfCancellationRequested();
         }
         catch (OperationCanceledException oc)
         {
             Start();
         }
     }, CancellationToken);

 }

 public void ForceUpdate()
 {
     CancellationTokenSource.Cancel();
 }           

还不确定在任务内部调用 Start() 会如何影响资源?我猜这很好,因为新任务将被赋予一个线程来执行?

Also not sure how calling Start() inside the task will affect resources? I'm guessing it's fine since the new task will be given a thread to execute in?

我想做这样的事情:

public void ForceUpdate()
{
    CancellationTokenSource.Cancel();
    ASMTask.WaitForCancellationComplete();
    Start();
}

但是我看不到通过取消等待任务完成的方法.

But I can't see a way to wait for the Task to complete by cancellation.

RE - 重复问题.

上一个问题的公认答案是以我试图避免的相同方式使用异常,但事实证明第二个答案很有用,但直到阅读了 <提供的解释后我才意识到这一点a href="https://stackoverflow.com/users/106159/matthew-watson">马修·沃森.我很高兴将此作为副本关闭,尽管我不知道如何实际做到这一点!

The accepted answer to the previous question was using exceptions in the same way that I was trying to avoid, it does turn out however that the second answer was useful but I didn't realise that until after reading the explanation provided by Matthew Watson. I'm happy for this to be closed as a duplicate, although I can't figure out how to actually do that!

推荐答案

这里有几个问题,所以我打算提出一个不同的方法.

There are a few issues here, so I'm going to propose a different approach.

首先,我注意到对 CheckForUpdate()Update() 的调用是同步的,所以使用 await 可能没有那么有用> 对于延迟 - 所以我将使用不同的延迟方式,同时仍然允许延迟被中断.

Firstly, I note that the calls to CheckForUpdate() and Update() are synchronous, so it's probably not that useful to use await for the delay - so I'll use a different way of delaying while still allowing the delay to be interrupted.

我还将主要方法分为两个 - 一个外部控制循环和一个内部处理循环.

I'll also split the main method into two - an outer controlling loop and an inner processing loop.

你没有提到任何控制外循环寿命的方法,所以我将使用取消令牌来控制它.

You don't mention any way to control the longevity of the outer loop, so I'll use a cancellation token to control that.

我已将其放在一个可编译的控制台应用程序中,以演示该方法.请注意,因为这是一个控制台应用程序,所以我不会等待用于启动控制循环的任务.在实际代码中,您会在某处等待.

I've put this together in a compilable console app that demonstrates the approach. Note that because this is a console app, I'm not awaiting the task I used to start the Control Loop. In real code, you'd await this somewhere.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication3
{
    class Program
    {
        static AutoResetEvent _restart = new AutoResetEvent(false);

        static void Main()
        {
            CancellationTokenSource cancellationSource = new CancellationTokenSource(10000); // Cancels after 10s.
            var task = Task.Run(() => ControlLoop(2, cancellationSource.Token, _restart));

            // After 5 seconds reset the process loop.

            Thread.Sleep(5000);
            Console.WriteLine("Restarting process loop.");
            RestartProcessLoop();

            // The cancellation source will cancel 10 seconds after it was constructed, so we can just wait now.
            Console.WriteLine("Waiting for control loop to terminate");
            task.Wait();

            Console.WriteLine("Control loop exited.");
        }

        public static void RestartProcessLoop()
        {
            _restart.Set();
        }

        public static async Task ControlLoop(int pollingIntervalSeconds, CancellationToken cancellation, AutoResetEvent restart)
        {
            while (!cancellation.IsCancellationRequested)
            {
                await Task.Run(() => ProcessLoop(pollingIntervalSeconds, cancellation, restart));
            }
        }

        public static void ProcessLoop(int pollingIntervalSeconds, CancellationToken cancellation, AutoResetEvent restart)
        {
            Console.WriteLine("Beginning ProcessLoop()");

            var previousDateTime = DateTime.MinValue;

            var terminators = new[]{cancellation.WaitHandle, restart};

            while (WaitHandle.WaitAny(terminators, TimeSpan.FromSeconds(pollingIntervalSeconds)) == WaitHandle.WaitTimeout)
            {
                if (CheckForUpdate())
                {
                    Update(previousDateTime);
                    previousDateTime = DateTime.Now;
                }
            }

            Console.WriteLine("Ending ProcessLoop()");
        }

        public static void Update(DateTime previousDateTime)
        {
        }

        public static bool CheckForUpdate()
        {
            Console.WriteLine("Checking for update.");
            return true;
        }
    }
}

这篇关于如何取消和重新启动 C# 任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆