如何取消和重新启动 C# 任务
How to cancel and restart a C# Task
我有一个很长 运行、间隔很长的轮询过程。我需要能够强制更新并重新启动轮询。
想到的最明显的事情是取消轮询任务,并开始一个新的任务,因为初始循环总是更新。
我正在尝试找出执行此操作的最佳方法,使用 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 - 重复问题。
上一个问题的公认答案是以我试图避免的相同方式使用异常,但事实证明第二个答案很有用,但直到阅读提供的解释后我才意识到这一点通过 Matthew Watson。
我很高兴将其作为副本关闭,尽管我不知道该怎么做!
这里有几个问题,所以我要提出一个不同的方法。
首先,我注意到对 CheckForUpdate()
和 Update()
的调用是同步的,因此使用 await
延迟可能不是很有用 - 所以我将使用不同的延迟方式,同时仍然允许延迟被中断。
我还将主要方法分为两个 - 一个外部控制循环和一个内部处理循环。
你没有提到任何控制外循环寿命的方法,所以我将使用取消令牌来控制它。
我已将其放在一个可编译的控制台应用程序中以演示该方法。请注意,因为这是一个控制台应用程序,所以我没有等待我用来启动控制循环的任务。在实际代码中,您会在某个地方等待它。
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;
}
}
}
您可以将 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;
}
}
}
我有一个很长 运行、间隔很长的轮询过程。我需要能够强制更新并重新启动轮询。
想到的最明显的事情是取消轮询任务,并开始一个新的任务,因为初始循环总是更新。
我正在尝试找出执行此操作的最佳方法,使用 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 - 重复问题。
上一个问题的公认答案是以我试图避免的相同方式使用异常,但事实证明第二个答案很有用,但直到阅读提供的解释后我才意识到这一点通过 Matthew Watson。 我很高兴将其作为副本关闭,尽管我不知道该怎么做!
这里有几个问题,所以我要提出一个不同的方法。
首先,我注意到对 CheckForUpdate()
和 Update()
的调用是同步的,因此使用 await
延迟可能不是很有用 - 所以我将使用不同的延迟方式,同时仍然允许延迟被中断。
我还将主要方法分为两个 - 一个外部控制循环和一个内部处理循环。
你没有提到任何控制外循环寿命的方法,所以我将使用取消令牌来控制它。
我已将其放在一个可编译的控制台应用程序中以演示该方法。请注意,因为这是一个控制台应用程序,所以我没有等待我用来启动控制循环的任务。在实际代码中,您会在某个地方等待它。
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;
}
}
}
您可以将 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;
}
}
}