4ZM*_*4ZM 6 c# multithreading cancellation
所有这些都是关于在C#中取消任务的复杂案例的设计/最佳实践的问题.如何实现共享任务的取消?
作为一个最小的例子,我们假设以下内容; 我们有一个长期运行,可合作取消的"工作"操作.它接受取消令牌作为参数,如果已被取消则抛出.它在某个应用程序状态下运行并返回一个值.其结果由两个UI组件独立完成.
虽然应用程序状态未更改,但应缓存Work函数的值,并且如果一个计算正在进行,则新请求不应该开始第二次计算,而是开始等待结果.
任何一个UI组件都应该能够取消它的任务,而不会影响其他UI组件任务.
你到目前为止和我在一起吗?
上面的内容可以通过在TaskCompletionSources中引入一个包装真实Work任务的Task缓存来完成,其任务:然后返回给UI组件.如果UI组件取消了它的Task,它只会放弃TaskCompletionSource Task而不是底层任务.这一切都很好.UI组件创建CancellationSource,取消请求是正常的自顶向下设计,底部是协作TaskCompletionSource任务.
现在,到了真正的问题.应用程序状态更改时该怎么办?让我们假设让"工作"功能在状态副本上运行是不可行的.
一种解决方案是监听任务缓存中的状态变化(或那里).如果缓存具有底层任务使用的CancellationToken,即运行Work函数的任务,则可以取消它.然后,这可以触发取消所有附加的TaskCompletionSources Task:s,因此两个UI组件都将获得Canceled任务.这是一种自下而上的取消.
有没有一种首选的方法呢?是否有一种设计模式将其描述在哪里?
可以实现自下而上取消,但感觉有点奇怪.UI任务是使用CancellationToken创建的,但由于另一个(内部)CancellationToken而被取消.此外,由于令牌不相同,因此不能在UI中忽略OperationCancelledException - 这会(最终)导致在外部Task:s终结器中抛出异常.
听起来您想要一组贪婪的任务操作 - 您有一个任务结果提供程序,然后构造一个任务集以返回第一个完成的操作,例如:
// Task Provider - basically, construct your first call as appropriate, and then
// invoke this on state change
public void OnStateChanged()
{
if(_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
_task = Task.Factory.StartNew(() =>
{
// Do Computation, checking for cts.IsCancellationRequested, etc
return result;
});
}
// Consumer 1
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
var waitForResultTask = Task.Factory.StartNew(() =>
{
// Internally, this is invoking the task and waiting for it's value
return MyApplicationState.GetComputedValue();
});
// Note this task cares about being cancelled, not the one above
var cancelWaitTask = Task.Factory.StartNew(() =>
{
while(!cts.IsCancellationRequested)
Thread.Sleep(25);
return someDummyValue;
});
Task.WaitAny(waitForResultTask, cancelWaitTask);
if(cancelWaitTask.IsComplete)
return "Blah"; // I cancelled waiting on the original task, even though it is still waiting for it's response
else
return waitForResultTask.Result;
});
Run Code Online (Sandbox Code Playgroud)
现在,我还没有完全测试这一点,但它应该允许您通过取消令牌来“取消”等待任务(从而强制“等待”任务首先完成并点击)WaitAny,并允许您“取消” “计算任务。
另一件事是找出一种干净的方法,让“取消”任务等待,而不会出现严重的阻塞。我认为这是一个好的开始。
| 归档时间: |
|
| 查看次数: |
932 次 |
| 最近记录: |