2

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

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

最后,我有一个任务启动方法的小例子。

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

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

// added code at the bottom of this question

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

  1. 用户在数据网格中选择对象
  2. ViewModel 需要数据并向方法/类/foo 请求数据。
  3. 任务(A)开始
  4. 任务(A)仍在工作。用户选择不同的对象/行。
  5. 重复步骤 1、2 和 3。所以任务(A)应该被取消/停止,一个新的任务(B)开始。
  6. Task(B) 完成后,应显示其数据。绝不应提供 Task(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这怎么可能?我对取消令牌做错了吗?

4

3 回答 3

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

我不完全确定您的问题是什么,但这应该可以帮助您了解一些可用的选项。

我希望这有帮助。

于 2012-09-26T07:59:31.270 回答
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;
            }
        });
    }
于 2012-09-28T07:00:56.177 回答
0

一种方法是使用CancellationToken.ThrowIfCancellationRequested Methodwhich 当被调用时会抛出一个OperationCanceledException如果令牌有一个取消请求

你可以像这样使用它

    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 question about why Cancellation token is required in Task constructor

于 2012-09-26T07:42:09.110 回答