2012-09-26 85 views
2

我正在使用任务从数据库等中读取一些数据。让我们假设我无法更改Dataaccess-API/Layer。如何取消任务,使其停止

此数据访问有时可能需要一些时间(网络流量等)。它将在用户更改选定项目或更改显示可变数据的子集的筛选器时加载。

最后我有一个我的Task-Start-Method的小例子。

我的问题是:如果用户在任务仍在运行时更改过滤器/选择,我该如何阻止它运行?当使用取消令牌时,它会完成(因为我没有在任务中使用“大”循环,我不能只检查.IsCancelled的每次迭代。

我的想法是使用任务的返回类型填补了SelectableVolts和分配新值之前检查返回任务上IsCancelled但如何为异步任务做到这一点

// added code at the bottom of this question 

更新:?获得评论等之后,“我不能完全确定你的问题是要求,但这应该有助于你感受一些可行的选择。“我会试着澄清我的问题一点,至少我希望如此;)

  1. 用户在数据网格
  2. 视图模型需要数据并且请求数据的方法/类/ foo的选择对象。
  3. 任务(A)开始
  4. 任务(A)仍在工作。用户选择不同的对象/行。
  5. 重复步骤1,2和3。所以任务(A)应该被取消/停止并且新的任务(B)开始。
  6. 当任务(B)完成时,应显示其数据。绝不应该让任务(A)的数据可用。

所以问题是:如何以正确的方式实现步骤5和步骤6。

全码:

private CancellationToken cancelTokenOfFilterTask; 
    private CancellationTokenSource cancelTokenSourceOfFilterTask; 

private void FilterVolts() 
{ 
    if (IsApplicationWorking) 
    { 
     cancelTokenSourceOfFilterTask.Cancel(); 
    } 

    // Prepare CancelToken 
    cancelTokenSourceOfFilterTask = new CancellationTokenSource(); 
    cancelTokenOfFilterTask = cancelTokenSourceOfFilterTask.Token; 

    IsApplicationWorking = true; 
    if (SelectableVolts != null && SelectableVolts.Count >= 0) 
    { 
     VoltThatIsSelected = null; 
     SelectableVolts.Clear(); 
    } 

    Task voltLoadTask = null; 
    voltLoadTask = Task.Factory.StartNew<List<SelectableVoltModel>>(() => { 
     VoltsLoader loader = new VoltsLoader(); 
     Thread.Sleep(2000); 
     var listOfVolts = loader.LoadVoltsOnFilter(_sourceOfCachableData, ChosenFilter); 
      return listOfVehicles; 
     }, cancelTokenOfFilterTask).ContinueWith(listSwitcher => 
     { 
      switch (listSwitcher.Status) 
      { 
       case TaskStatus.RanToCompletion: 
        SelectableVolts = new ObservableCollection<SelectableVoltsModel>(listSwitcher.Result); 
        IsApplicationWorking = false; 
        break; 
       case TaskStatus.Canceled: 
        Console.WriteLine("Cancellation seen"); // Gets never called 
        break;   
       default: 
        break; 
      } 
     }); 
    } 

不知怎的,当我把这种方法不止一次,都将在TaskStatus.RanTocompletion运行这怎么可能?我是否在取消令牌上出现错误?

+1

请将您自己的答案移出问题并转化为下面的实际答案(即回答自己的问题)。然后接受你自己的答案,如果它对你有用。 – tomfanning

+0

好的 - 我只是想让@Killercam感谢我向他展示正确的方式。但是你是对的,也许当标记为答案时,“真正的”答案会被发现。 – basti

回答

1

回答:我想我找到了解决方案,我必须在每次取消请求后重置CancellationTokenSource。

这是我根据接受的答案更新的代码。如果有什么问题,我很高兴有关任何信息:)

private CancellationToken cancelTokenOfFilterTask; 
    private CancellationTokenSource cancelTokenSourceOfFilterTask = new CancellationTokenSource(); 

    private void FilterVolts() 
    { 
     if (IsApplicationWorking) 
     { 
      cancelTokenSourceOfFilterTask.Cancel(); 
     } 

     // Prepare CancelToken 
     cancelTokenOfFilterTask = cancelTokenSourceOfFilterTask.Token; 

     IsApplicationWorking = true; 
     if (SelectableVolts != null && SelectableVolts.Count >= 0) 
     { 
      VoltThatIsSelected = null; 
      SelectableVolts.Clear(); 
     } 

     Task voltsLoadTask = null; 
     voltsLoadTask = Task.Factory.StartNew<List<SelectableVoltsModel>>(() => { 
      VehicleLoader loader = new VehicleLoader(); 
      Thread.Sleep(2000); // just for testing, so the task runs a "long" time 
      var listOfVolts = loader.LoadVoltsOnFilter(_sourceOfCachableData, ChosenFilter); 
      return listOfVolts; 
     }, cancelTokenOfFilterTask).ContinueWith(listSwitcher => 
     { 
      switch (listSwitcher.Status) 
      { 
       case TaskStatus.RanToCompletion: 
        SelectableVolts = new ObservableCollection<SelectableVoltModel>(listSwitcher.Result); 
        IsApplicationWorking = false; 
        break; 
       case TaskStatus.Canceled: 
        cancelTokenSourceOfFilterTask = new CancellationTokenSource(); // reset Source 
        break; 
       default: 
        break; 
      } 
     }); 
    } 
0

一个方法是使用哪个时调用将抛出OperationCanceledException如果令牌有相关的要求取消订单CancellationToken.ThrowIfCancellationRequested Method

,你可以使用它像这样

var cancelTokenSourceOfFilterTask = new CancellationTokenSource(); 
    var cancelTokenOfFilterTask = cancelTokenSourceOfFilterTask.Token; 

    Task.Factory.StartNew(() => 
    { 
     VoltLoader vl = new VoltLoader(); 

     //if a request is raised for cancellation then an exception will be thrown 
     cancelTokenOfFilterTask.ThrowIfCancellationRequested(); 

     var listOfVolts = vl.LoadVoltsOnFilter(_sourceOfCachableData, ChosenFilter); 

     SelectableVolts = new ObservableCollection<SelectableVoltsModel>(listOfVolts); 
    }, cancelTokenOfFilterTask); 

上面的代码是相同的如下面的代码片断

var cancelTokenSourceOfFilterTask = new CancellationTokenSource(); 
    var cancelTokenOfFilterTask = cancelTokenSourceOfFilterTask.Token; 

    Task.Factory.StartNew(() => 
    { 
     VoltLoader vl = new VoltLoader(); 

     //if a request is raised for cancellation then an exception will be thrown 
     if (cancelTokenOfFilterTask.IsCancellationRequested) 
      throw new OperationCanceledException(cancelTokenOfFilterTask); 

     var listOfVolts = vl.LoadVoltsOnFilter(_sourceOfCachableData, ChosenFilter); 

     SelectableVolts = new ObservableCollection<SelectableVoltsModel>(listOfVolts); 
    }, cancelTokenOfFilterTask); 

上面的仅仅是可以如何被使用,更示例这可以SO阅读本阙关于why Cancellation token is required in Task constructor

+0

但是如果在if语句之后取消,它会被调用吗?所以它在'vl.LoadVoltsOnFilter'之后被取消了? – basti

+0

不,我不会这么做,我的建议是在任何繁重的操作之前使用它,如果您需要,也可以多次使用相同的代码片段。 – Vamsi

+0

AFAIK这不适用于所有情况。如果任务被取消,它将发生,该任务在最后一个if(isCancelled)返回false之前结束。在并行执行中错误的定时上下文切换将足以满足此... – basti

1

我不确定为什么你不能在循环中包含所需的取消支持。为此,您只需将cancelTokenOfFilterTask传入您在“StartNew”委托中调用的方法即可。然后,该方法中,你可以做

token.ThrowIfCancellationRequested(); 

要检查Task是否已成功地完成和处理培训相关的结果,使用延续

cancelTokenSourceOfFilterTask = new CancellationTokenSource(); 
cancelTokenOfFilterTask = cancelTokenSourceOfFilterTask.Token; 

Task task = null; 
task = Task.Factory.StartNew(() => 
{ 
    VoltLoader vl = new VoltLoader(); 
    var listOfVolts = vl.LoadVoltsOnFilter(_sourceOfCachableData, ChosenFilter); 
    SelectableVolts = new ObservableCollection<SelectableVoltsModel>(listOfVolts); 
}, cancelTokenOfFilterTask); 

task.ContinueWith(ant => 
{ 
    switch (ant.Status) 
    { 
     // Handle any exceptions to prevent UnobservedTaskException.    
     case TaskStatus.RanToCompletion: 
      if (asyncTask.Result) 
      { 
       // Do stuff... 
      } 
      else 
      { 
       // Do stuff... 
      } 
      break; 
     case TaskStatus.Canceled: 
      // If Cancelled you can start the task again reading the new settings. 
      break; 
     case TaskStatus.Faulted: 
      break; 
    } 
}, CancellationToken.None, 
    TaskContinuationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext()); 

我不能完全确定你的问题是要求,但这应该有助于您了解一些可用的选项。

我希望这会有所帮助。

+0

ContinueWith的想法看起来很有希望。我将如何在一个方法中使用它?所以我每次用户在数据网格中选择另一行时,都会调用loadNeededData方法。当用户更改_before_行时,旧任务完成后,我需要取消它。每个任务是否会在同一个ContiuneWith中运行,以便我可以简单地使用您的switch-statement? – basti

+0

在调用'token.ThrowIfCancellationRequested()'之前,您不需要检查token.IsCancellationRequested,因为方法假设只会引发一个异常'token.IsCancellationRequested'为'true',则您编写的代码等同于检查相同'token.IsCancellationRequested'两次,然后抛出异常 – Vamsi

+0

请参阅编辑...当然,这是一个错误。 – MoonKnight