C# 任务取消不停止任务执行

C# 任务取消不停止任务执行,c#,task,C#,Task,我有一个任务,它查询active directory并用结果填充列表。我已经设置了可以取消的任务,但是,当调用cancel时,任务将继续执行其工作。我知道任务已被取消,因为它将返回,并且计划在任务返回时执行的操作正在运行,但查询将使用内存和处理能力在后台继续运行。任务可以反复启动和“取消”,每次任务迭代都会运行并使用资源。我怎样才能使取消实际上取消 视图模型 ActiveDirectoryQuery 公共异步任务执行() { _cancellationTokenSource=新的cancella

我有一个
任务
,它查询active directory并用结果填充列表。我已经设置了可以取消的任务,但是,当调用cancel时,任务将继续执行其工作。我知道任务已被取消,因为它将返回,并且计划在任务返回时执行的操作正在运行,但查询将使用内存和处理能力在后台继续运行。任务可以反复启动和“取消”,每次任务迭代都会运行并使用资源。我怎样才能使取消实际上取消

视图模型 ActiveDirectoryQuery
公共异步任务执行()
{
_cancellationTokenSource=新的cancellationTokenSource();
var taskCompletionSource=新的taskCompletionSource();
_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();
}
数据=获取数据(数据制备者);
},
_cancellationTokenSource.Token);
等待任务.WhenAny(任务,任务完成源.Task);
}
公开作废取消()
{
_cancellationTokenSource?.Cancel();
}
Cancel()
由绑定到
按钮的
命令调用。执行该任务可能需要几分钟,并且可能会消耗数百兆的RAM。如果有帮助,我可以提供任何引用的方法或任何其他信息。

,如果要取消操作,则需要编辑要取消的函数。因此,执行将成为

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);
   }
}
公共异步任务执行()
{
_cancellationTokenSource=新的cancellationTokenSource();
var taskCompletionSource=新的taskCompletionSource();
//令牌注册完成后需要处理。
使用(_cancellationTokenSource.Token.Register(
()=>taskCompletionSource.TrySetCanceled())
{
DataPreparer DataPreparer=null;
var task=task.Run(()=>
{
if(QueryTypeIsOu())
{
dataPreparer=SetUpOuDataPreparer(_cancellationTokenSource.Token);
}
else if(QueryTypeIsContextComputer())
{
dataPreparer=SetUpComputerDataPreparer(\u cancellationTokenSource.Token);
}
else if(QueryTypeIsContextDirectReportOrUser())
{
dataPreparer=SetUpDirectReportOrUserDataPreparer(\u cancellationTokenSource.Token);
}
else if(QueryTypeIsContextGroup())
{
dataPreparer=SetUpGroupDataPreparer(\u cancellationTokenSource.Token);
}
Data=GetData(dataPreparer,_cancellationTokenSource.Token);
},
_cancellationTokenSource.Token);
等待任务.WhenAny(任务,任务完成源.Task);
}
}
然后从这些方法内部。如果这些函数中有循环,则需要从循环内部调用。如果不循环并调用某个外部API,则需要使用该API的取消方法,希望API将接受
CancellationToken
,如果不接受,则需要调用
。Cancel()
方法使用
Register
方法,就像在
Execute
中一样


如果API没有公开取消查询的方法,那么提前停止查询的唯一安全方法是将查询移动到单独的exe。执行查询时,执行
var proc=Process.Start(…)
以启动exe。要与之通信,请使用某种形式的IPC,例如,您可以在进程开始之前生成一个
Guid
,并将其作为参数传入,然后使用该Guid作为命名管道的名称。如果需要提前结束查询,请执行
proc.Kill()
以结束外部进程。

需要查询任务内令牌的
IsCancellationRequested
属性,并相应地从任务返回
Cancel
不只是“杀死”任务,它只是将该属性的值设置为true。@KDecker我想你的意思是我需要重复查询它?考虑到实际工作需要采用几种更深入的方法,这是不实际的,但我的设计可能是真正的问题。@MichaelBrandonMorris是的,你需要进行测试。您还可以使用ThrowIfCancellationRequested()
也可以查看@ConradFrix,以便任务调用方法(如上所示),每个方法都调用一个静态方法,该静态方法可以调用其他静态方法。在任何情况下,工作的冲击都发生在最深层,最深层需要返回一个对象。如何在允许返回对象向上传播的同时向下传播取消标记?@MichaelBrandonMorris取消标记与其他标记一样是一个变量。使变量可用于静态方法的方法是相同的。通常这意味着将令牌传递到每个方法中。谢谢。代码是我自己的,因此我检查并修改了其中的每个方法,以接受
CancellationToken
。我就是这样做的,我认为这是唯一的方法。查询属性/传递令牌看起来确实很麻烦。我一直想问一个问题,是否有一个更干净的方法,但我怀疑它是如何工作的这种方法也很好,因为您可以取消“深入”计算,而不是等待长时间运行的方法完成并在“任务级别”取消。
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();
}
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);
   }
}