如何取消任务以使其停止

时间:2012-09-26 07:23:19

标签: c# .net-4.0 task-parallel-library

我正在使用Task从数据库中读取一些数据等。我们假设我无法更改Dataaccess-API / -Layer。

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

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

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

我的想法是使用任务的返回类型填充SelectableVolts并在分配新值之前检查IsCancelled上的返回任务。但是如何为异步任务执行此操作?

// added code at the bottom of this question

更新:收到评论后“我不完全确定您的问题要求什么,但这可以帮助您了解一些可用的选项。”我会尽力澄清我的问题。至少我希望如此;)

  1. 用户在datagrid中选择对象
  2. ViewModel需要数据并向data / class / foo询问数据。
  3. 任务(A)已启动
  4. 任务(A)仍然有效。用户选择不同的对象/行。
  5. 重复步骤1,2和3。因此应取消/停止任务(A)并启动新的任务(B)。
  6. 当任务(B)完成时,应显示其数据。绝不应该提供任务(A)的数据。
  7. 所以问题是:如何以正确的方式实现步骤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中运行,这怎么可能?我在取消令牌时遇到了什么问题吗?

3 个答案:

答案 0 :(得分: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());

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

我希望这会有所帮助。

答案 1 :(得分: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;
            }
        });
    }

答案 2 :(得分:0)

一种方法是使用CancellationToken.ThrowIfCancellationRequested Method,如果令牌有取消请求,则OperationCanceledException会抛出why Cancellation token is required in Task constructor

你可以像这样使用它

    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);

上面只是一个如何使用它的示例,为此您可以阅读有关{{3}}

的这个问题。