Jen*_*ens 47 c# multithreading cancellation-token
我目前正在改进我们长期运行的方法,以便取消.我打算使用System.Threading.Tasks.CancellationToken来实现它.
我们的方法通常执行一些长时间运行的步骤(主要向硬件发送命令然后等待硬件),例如
void Run()
{
Step1();
Step2();
Step3();
}
Run Code Online (Sandbox Code Playgroud)
我对取消的第一个(也许是愚蠢的)想法会将其转化为
bool Run(CancellationToken cancellationToken)
{
Step1(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step2(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step3(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
return true;
}
Run Code Online (Sandbox Code Playgroud)
坦率地看起来很可怕.这种"模式"也将在单个步骤内继续(并且它们必然已经相当长).这会使Thread.Abort()看起来很性感,虽然我知道它不推荐.
有没有更清晰的模式来实现这一点,不会隐藏许多样板代码下面的应用程序逻辑?
编辑
作为步骤性质的一个例子,该Run方法可以阅读
void Run()
{
GiantRobotor.MoveToBase();
Oven.ThrowBaguetteTowardsBase();
GiantRobotor.CatchBaguette();
// ...
}
Run Code Online (Sandbox Code Playgroud)
我们正在控制需要同步的不同硬件单元才能协同工作.
Mat*_*ten 33
如果这些步骤以某种方式独立于方法中的数据流,但不能以并行的方式执行,则以下方法可能更易读:
void Run()
{
// list of actions, defines the order of execution
var actions = new List<Action<CancellationToken>>() {
ct => Step1(ct),
ct => Step2(ct),
ct => Step3(ct)
};
// execute actions and check for cancellation token
foreach(var action in actions)
{
action(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
如果这些步骤不需要取消令牌,因为您可以将它们分成很小的单元,您甚至可以编写一个较小的列表定义:
var actions = new List<Action>() {
Step1, Step2, Step3
};
Run Code Online (Sandbox Code Playgroud)
Nic*_*sen 23
怎么样延续?
var t = Task.Factory.StartNew(() => Step1(cancellationToken), cancellationToken)
.ContinueWith(task => Step2(cancellationToken), cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current)
.ContinueWith(task => Step3(cancellationToken), cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
Run Code Online (Sandbox Code Playgroud)
我承认它不漂亮,但指导是做你做过的事情:
if (cancellationToken.IsCancellationRequested) { /* Stop */ }
Run Code Online (Sandbox Code Playgroud)
......或略短:
cancellationToken.ThrowIfCancellationRequested()
Run Code Online (Sandbox Code Playgroud)
通常,如果您可以将取消令牌传递给各个步骤,则可以将签出分散,以免它们使代码饱和.您也可以选择不检查消除不断 ; 如果您正在执行的操作是幂等的并且不是资源密集型的,那么您不一定要在每个阶段检查取消.最重要的检查时间是在返回结果之前.
如果您将令牌传递给所有步骤,则可以执行以下操作:
public static CancellationToken VerifyNotCancelled(this CancellationToken t) {
t.ThrowIfCancellationRequested();
return t;
}
...
Step1(token.VerifyNotCancelled());
Step2(token.VerifyNotCancelled());
Step3(token.VerifyNotCancelled());
Run Code Online (Sandbox Code Playgroud)
当我不得不做这样的事情时,我创建了一个委托来做:
bool Run(CancellationToken cancellationToken)
{
var DoIt = new Func<Action<CancellationToken>,bool>((f) =>
{
f(cancellationToken);
return cancellationToken.IsCancellationRequested;
});
if (!DoIt(Step1)) return false;
if (!DoIt(Step2)) return false;
if (!DoIt(Step3)) return false;
return true;
}
Run Code Online (Sandbox Code Playgroud)
或者,如果步骤之间从未有任何代码,您可以写:
return DoIt(Step1) && DoIt(Step2) && DoIt(Step3);
Run Code Online (Sandbox Code Playgroud)