【问题标题】:How to cancel and restart a C# Task [duplicate]如何取消和重新启动 C# 任务 [重复]
【发布时间】:2017-03-02 14:51:59
【问题描述】:

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

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

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

 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() 会如何影响资源?我猜这很好,因为新任务将被赋予一个线程来执行?

我想做这样的事情:

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

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


编辑:RE - 重复问题。

上一个问题的公认答案是以我试图避免的相同方式使用异常,但事实证明第二个答案很有用,但直到阅读@提供的解释后我才意识到这一点987654321@。 我很高兴将其作为副本关闭,尽管我不知道如何实际做到这一点!

【问题讨论】:

    标签: c# multithreading task cancellation-token


    【解决方案1】:

    这里有一些问题,所以我将提出一种不同的方法。

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

    我还将 main 方法分成两个 - 一个外部控制循环和一个内部处理循环。

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

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

    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;
            }
        }
    }
    

    【讨论】:

    • 非常有帮助,谢谢。在我的情况下需要做while,因为我希望代码在激活时立即执行一次。
    • @DavidKirkpatrick 我想您可以根据需要进行调整。 :)
    【解决方案2】:

    您可以将Start 设为async 方法(我在其中添加了一行来模拟错误异常)

        static int count;
        public async Task Start()
        {
            var previousDateTime = DateTime.MinValue;
            CancellationTokenSource = new CancellationTokenSource();
            CancellationToken = CancellationTokenSource.Token;
            try
            {
                while (!CancellationToken.IsCancellationRequested)
                {
                    if (CheckForUpdate())
                    {
                        Update(previousDateTime); // or better await UpdateAsync(previousDateTime);
                    }
    
                    await Task.Delay(PollingInterval * 1000, CancellationToken);
                    Debug.WriteLine("here " + count);
                    if (count>3)
                    {
                        count = 0;
                        throw new Exception("simulate error");
                    }
                }
                CancellationToken.ThrowIfCancellationRequested();
            }
            catch (OperationCanceledException oc)
            {
                Debug.WriteLine(oc.Message);
            }
        }
    

    然后从像这样的事件中调用它

        private async void button_Click(object sender, RoutedEventArgs e)
        {
    
            ASMTask = Start();
            await ASMTask;
        }
    

    要取消并重新启动Task,请使用

        public async Task ForceUpdate()
        {
            CancellationTokenSource.Cancel();
            await ASMTask;
            count++;
            ASMTask = Start();
            await ASMTask;
        }
    

    再次来自事件处理程序

        private async void button1_Click(object sender, RoutedEventArgs e)
        {
            if (ASMTask != null)
            {
                try
                {
                    await ForceUpdate();
                }
                catch (Exception exc)
                {
                    Debug.Write(exc.Message);
                    ASMTask = Start();
                    await ASMTask;
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 2013-10-09
      • 1970-01-01
      • 1970-01-01
      • 2012-09-19
      • 1970-01-01
      相关资源
      最近更新 更多