C# 如何从工作线程轮询取消
我有一个UI,它由一个后台工作线程生成,该线程执行一个复杂的任务树和子任务,大约需要一分钟才能完成 一个要求是后台工作任务必须能够在其开始后被取消 目前,我的解决方案还很幼稚,把代码弄得一团糟。在UI中按下取消按钮时,将设置取消令牌。工作线程定期(在任务之间)轮询此令牌,如果已设置,则它将退出:C# 如何从工作线程轮询取消,c#,multithreading,cancellation,C#,Multithreading,Cancellation,我有一个UI,它由一个后台工作线程生成,该线程执行一个复杂的任务树和子任务,大约需要一分钟才能完成 一个要求是后台工作任务必须能够在其开始后被取消 目前,我的解决方案还很幼稚,把代码弄得一团糟。在UI中按下取消按钮时,将设置取消令牌。工作线程定期(在任务之间)轮询此令牌,如果已设置,则它将退出: void ThreadWorkerHandler(CancelToken cancelToken) { DoTask1(cancelToken); if (cancelToken.IsS
void ThreadWorkerHandler(CancelToken cancelToken)
{
DoTask1(cancelToken);
if (cancelToken.IsSet)
return;
DoTask2(cancelToken);
if (cancelToken.IsSet)
return;
DoTask3(cancelToken);
if (cancelToken.IsSet)
return;
DoTask4(cancelToken);
}
void DoTask2(CancelToken cancelToken)
{
DoSubTask2a();
if (cancelToken.IsSet)
return;
DoSubTask2b();
if (cancelToken.IsSet)
return;
DoSubTask2c();
if (cancelToken.IsSet)
return;
}
有更好的解决办法吗?我在想一个类似SoLongAs语句的东西,它会自动插入检查,并在满足条件时自动引发内部异常,该异常会在循环结束时被内部捕获,例如:
void ThreadWorkerHandler(CancelToken cancelToken)
{
SoLongAs (canelToken.IsSet == false)
{
DoTask1(cancelToken);
DoTask2(cancelToken);
DoTask3(cancelToken);
DoTask4(cancelToken);
}
}
但我想,出于某种原因,这是行不通的,更重要的是,我怀疑这样的事情是否真的存在。如果没有,有没有比我目前使用的更好的方法来处理这个场景?谢谢。如果您有一组代表您的工作的委托,那么您可以得到一些与您的代码片段非常接近的内容。它的开销比您预期的语法要多一些,但关键是它是一个恒定的开销,而不是每行的开销
List<Action> actions = new List<Action>()
{
()=> DoTask1(cancelToken),
()=> DoTask2(cancelToken),
()=> DoTask3(cancelToken),
()=> DoTask4(cancelToken),
};
foreach(var action in actions)
{
if (!cancelToken.IsSet)
action();
}
List actions=new List()
{
()=>DoTask1(取消令牌),
()=>DoTask2(取消令牌),
()=>DoTask3(取消令牌),
()=>DoTask4(取消令牌),
};
foreach(行动中的var行动)
{
如果(!cancelToken.IsSet)
动作();
}
如果您有一组代表您的工作的委托,那么您可以得到与您的代码片段非常接近的内容。它的开销比您预期的语法要多一些,但关键是它是一个恒定的开销,而不是每行的开销
List<Action> actions = new List<Action>()
{
()=> DoTask1(cancelToken),
()=> DoTask2(cancelToken),
()=> DoTask3(cancelToken),
()=> DoTask4(cancelToken),
};
foreach(var action in actions)
{
if (!cancelToken.IsSet)
action();
}
List actions=new List()
{
()=>DoTask1(取消令牌),
()=>DoTask2(取消令牌),
()=>DoTask3(取消令牌),
()=>DoTask4(取消令牌),
};
foreach(行动中的var行动)
{
如果(!cancelToken.IsSet)
动作();
}
您可以使用CancellationToken.throwifccancellationRequested()
。如果设置了令牌,这将引发异常
也可以考虑使用<代码> TPL <代码>任务< /代码>。所有子任务都可以使用相同的
CancellationToken
一个接一个地链接,这将简化代码,因为TPL
框架在调用continuation之前会注意检查Token
状态
您的代码如下所示:
Task.Factory.StartNew(DoTask1,cancelationToken)
.ContinueWith(t=>DoTask2(),cancelationToken)
.ContinueWith(t=>DoTask3(),cancelationToken)
.ContinueWith(t=>DoTask4(),cancelationToken)
注意此解决方案假定DoTask
不会引发除操作取消异常
之外的其他异常
Note2您不必在任务/子任务正文内调用
ThrowIfCancellationRequested()
<代码>TPL将在调用任何continuations之前自动检查令牌状态。但是您可以使用此方法中断任务/子任务的执行。您可以使用取消令牌。ThrowIfCancellationRequested()
。如果设置了令牌,这将引发异常
也可以考虑使用<代码> TPL <代码>任务< /代码>。所有子任务都可以使用相同的
CancellationToken
一个接一个地链接,这将简化代码,因为TPL
框架在调用continuation之前会注意检查Token
状态
您的代码如下所示:
Task.Factory.StartNew(DoTask1,cancelationToken)
.ContinueWith(t=>DoTask2(),cancelationToken)
.ContinueWith(t=>DoTask3(),cancelationToken)
.ContinueWith(t=>DoTask4(),cancelationToken)
注意此解决方案假定DoTask
不会引发除操作取消异常
之外的其他异常
Note2您不必在任务/子任务正文内调用
ThrowIfCancellationRequested()
<代码>TPL将在调用任何continuations之前自动检查令牌状态。但是您可以使用此方法中断任务/子任务的执行。Servy的想法非常好。我只是偷了它(要归功于他!),并演示了如何将它与列表的扩展方法一起使用。我完全理解任何认为这“太可爱”的人,但我认为它有一定的优雅
这里有一个练习,演示如何使用扩展方法。根据Servy的想法,扩展获取一个动作委托列表,并依次运行每个动作委托,直到完成或取消
private static bool test(CancellationToken cancelToken)
{
return new List<Action>
{
doTask1,
doTask2,
doTask3,
doTask4,
() => Console.WriteLine("Press a key to exit.")
}
.Run(cancelToken);
}
专用静态布尔测试(CancellationToken cancelToken)
{
返回新列表
{
doTask1,
doTask2,
doTask3,
doTask4,
()=>Console.WriteLine(“按一个键退出”)
}
.运行(取消令牌);
}
下面是整个样本:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
internal class Program
{
private static void Main(string[] args)
{
CancellationTokenSource cancelSource = new CancellationTokenSource();
Console.WriteLine("Press any key to interrupt the work.");
var work = Task<bool>.Factory.StartNew(() => test(cancelSource.Token));
Console.ReadKey();
cancelSource.Cancel();
Console.WriteLine(work.Result ? "Completed." : "Interrupted.");
}
private static bool test(CancellationToken cancelToken)
{
return new List<Action>
{
doTask1,
doTask2,
doTask3,
doTask4,
() => Console.WriteLine("Press a key to exit.")
}
.Run(cancelToken);
}
private static void doTask1()
{
Console.WriteLine("Task 1 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask2()
{
Console.WriteLine("Task 2 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask3()
{
Console.WriteLine("Task 3 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask4()
{
Console.WriteLine("Task 4 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
}
public static class EnumerableActionExt
{
public static bool Run(this IEnumerable<Action> actions, CancellationToken cancelToken)
{
foreach (var action in actions)
{
if (!cancelToken.IsCancellationRequested)
{
action();
}
else
{
return false;
}
}
return true;
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
命名空间控制台应用程序2
{
内部课程计划
{
私有静态void Main(字符串[]args)
{
CancellationTokenSource cancelSource=新的CancellationTokenSource();
控制台。WriteLine(“按任意键中断工作”);
var work=Task.Factory.StartNew(()=>test(cancelSource.Token));
Console.ReadKey();
cancelSource.Cancel();
Console.WriteLine(work.Result?“已完成”。:“中断”);
}
专用静态布尔测试(CancellationToken cancelToken)
{
返回新列表
{
doTask1,