我正在使用Task从数据库中读取一些数据等。我们假设我无法更改Dataaccess-API / -Layer。
此数据访问有时可能需要一些时间(网络流量等)。当用户更改所选项目或更改显示可用数据子集的过滤器时,将加载它。
最后,我有一个关于Task-Start-Method的小例子。
我的问题是:如果用户在任务仍在运行时更改过滤器/选择,我该如何阻止其运行?当使用取消令牌时它将完成(因为我没有在任务中使用“大”循环,我不能只检查.IsCancelled
的每次迭代。
我的想法是使用任务的返回类型填充SelectableVolts
并在分配新值之前检查IsCancelled
上的返回任务。但是如何为异步任务执行此操作?
// added code at the bottom of this question
更新:收到评论后“我不完全确定您的问题要求什么,但这可以帮助您了解一些可用的选项。”我会尽力澄清我的问题。至少我希望如此;)
所以问题是:如何以正确的方式实现步骤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
中运行,这怎么可能?我在取消令牌时遇到了什么问题吗?
答案 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}}
的这个问题。