我有一个Task,它查询一个活动目录,并将结果填充到列表中。我已经设置了我的任务,以便可以取消它,但是,当调用cancel时,任务继续执行它的工作。我知道任务已被取消,因为它返回,并且在任务返回时要执行的操作被运行,但是查询仍然在后台运行,使用内存和处理能力。任务可以重复启动和“取消”,任务的每一次迭代都在运行并使用资源。我怎样才能使取消实际上取消呢?
ViewModel
private async Task RunQuery(QueryType queryType,
string selectedItemDistinguishedName = null)
{
StartTask();
try
{
_activeDirectoryQuery = new ActiveDirectoryQuery(queryType,
CurrentScope, selectedItemDistinguishedName);
await _activeDirectoryQuery.Execute();
Data = _activeDirectoryQuery.Data.ToDataTable().AsDataView();
CurrentQueryType = queryType;
}
catch (ArgumentNullException)
{
ShowMessage(
"No results of desired type found in selected context.");
}
catch (OutOfMemoryException)
{
ShowMessage("The selected query is too large to run.");
}
FinishTask();
}
private void CancelCommandExecute()
{
_activeDirectoryQuery?.Cancel();
}ActiveDirectoryQuery
public async Task Execute()
{
_cancellationTokenSource = new CancellationTokenSource();
var taskCompletionSource = new TaskCompletionSource<object>();
_cancellationTokenSource.Token.Register(
() => taskCompletionSource.TrySetCanceled());
DataPreparer dataPreparer = null;
var task = Task.Run(() =>
{
if (QueryTypeIsOu())
{
dataPreparer = SetUpOuDataPreparer();
}
else if (QueryTypeIsContextComputer())
{
dataPreparer = SetUpComputerDataPreparer();
}
else if (QueryTypeIsContextDirectReportOrUser())
{
dataPreparer = SetUpDirectReportOrUserDataPreparer();
}
else if (QueryTypeIsContextGroup())
{
dataPreparer = SetUpGroupDataPreparer();
}
Data = GetData(dataPreparer);
},
_cancellationTokenSource.Token);
await Task.WhenAny(task, taskCompletionSource.Task);
}
public void Cancel()
{
_cancellationTokenSource?.Cancel();
}Cancel()由绑定到Button的Command调用。这个任务可能需要几分钟才能执行,并且可以消耗几百兆字节的内存。如果有帮助,我可以提供任何引用的方法或任何其他信息。
发布于 2016-07-06 16:38:48
取消是合作的,如果要取消操作,则需要编辑函数以取消操作。所以执行会变成
public async Task Execute()
{
_cancellationTokenSource = new CancellationTokenSource();
var taskCompletionSource = new TaskCompletionSource<object>();
//Token registrations need to be disposed when done.
using(_cancellationTokenSource.Token.Register(
() => taskCompletionSource.TrySetCanceled()))
{
DataPreparer dataPreparer = null;
var task = Task.Run(() =>
{
if (QueryTypeIsOu())
{
dataPreparer = SetUpOuDataPreparer(_cancellationTokenSource.Token);
}
else if (QueryTypeIsContextComputer())
{
dataPreparer = SetUpComputerDataPreparer(_cancellationTokenSource.Token);
}
else if (QueryTypeIsContextDirectReportOrUser())
{
dataPreparer = SetUpDirectReportOrUserDataPreparer(_cancellationTokenSource.Token);
}
else if (QueryTypeIsContextGroup())
{
dataPreparer = SetUpGroupDataPreparer(_cancellationTokenSource.Token);
}
Data = GetData(dataPreparer, _cancellationTokenSource.Token);
},
_cancellationTokenSource.Token);
await Task.WhenAny(task, taskCompletionSource.Task);
}
}然后从这些方法里面。如果在这些函数中有循环,则需要从循环内部调用token.ThrowIfCancellationRequested()。如果不循环并调用一些外部API,则需要使用该API的取消方法,希望API将接受CancellationToken,如果不接受,则需要调用.Cancel()方法使用Register方法,就像在Execute中所做的那样。
如果API不公开取消查询的方法,那么安全早期停止查询的唯一方法是将查询移动到单独的exe。当您执行查询时,您需要执行一个var proc = Process.Start(...)来启动exe。要与它通信,可以使用某种形式的IPC (如WCF在命名管道上 ),您可以在进程开始之前生成一个Guid,并将它作为参数传入,然后使用该guid作为命名管道的名称。如果需要尽早结束查询,则需要执行proc.Kill()来结束外部进程。
https://stackoverflow.com/questions/38227614
复制相似问题