如何在C#中实现取消共享任务:s

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终结器中抛出异常.

Tej*_*ejs 1

听起来您想要一组贪婪的任务操作 - 您有一个任务结果提供程序,然后构造一个任务集以返回第一个完成的操作,例如:

// 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,并允许您“取消” “计算任务。

另一件事是找出一种干净的方法,让“取消”任务等待,而不会出现严重的阻塞。我认为这是一个好的开始。