如何在 PLINQ 中等待取消令牌注册方法

How to wait for Cancellation Token Register method within a PLINQ

正如您在我下面的代码中看到的那样,为每个 Employee 调用了 ProcessThisEmployee。在该方法中,我在取消操作时调用第三方库的 Clean 方法。 假设 Clean 方法需要相当多的时间。当 ProcessThisEmployee 方法的所有 运行 个实例的 Clean 方法完成时,我想做的是在 UI 中显示一些消息。 这意味着我需要等待所有 Clean 方法完成。现在,我有一个 WaitAll 任务,但我不确定它是否也会等待所有取消完成。有什么想法吗?

class ProcessEmployees
{
    private List<Employee> _Employees;
    CancellationTokenSource cs = new CancellationTokenSource();

    public ProcessEmployees()
    {
        _Employees = new List<Employee>() 
        {
            new Employee() { ID = 1, FirstName = "John", LastName = "Doe" },
            new Employee() { ID = 2, FirstName = "Peter", LastName = "Saul" },
            new Employee() { ID = 3, FirstName = "Mike", LastName = "Sue" },
            new Employee() { ID = 4, FirstName = "Catherina", LastName = "Desoza" },
            new Employee() { ID = 5, FirstName = "Paul", LastName = "Smith" }
        };
    }

    public void StartProcessing()
    {
        try
        {
            Task[] tasks = this._Employees.AsParallel().WithCancellation(cs.Token).Select(x => this.ProcessThisEmployee(x, cs.Token)).ToArray();
            Task.WaitAll(tasks);
        }
        catch (AggregateException ae)
        {
            // error handling code
        }
        // other stuff
    }

    private async Task ProcessThisEmployee(Employee x, CancellationToken token)
    {
        ThirdPartyLibrary library = new ThirdPartyLibrary();
        token.ThrowIfCancellationRequested();

        using(token.Register(() => library.Clean())
        {
            await Task.Factory.StartNew(() => library.SomeAPI(x) );
        }
    }
}

好吧,您可以使用 CountdownEvent 轻松等待所有这些。您在开始时设置它的大小,在每个 library.Clean() 之后发出信号并使用 Wait():

等待它达到 0
private CountdownEvent _countdownEvent;
public void Cancel()
{
    cs.Cancel();
    _countdownEvent.Wait();
    // Update UI
}

public void StartProcessing()
{
    try
    {
        _countdownEvent = new CountdownEvent(_Employees.Count);
        Task[] tasks = this._Employees.AsParallel().WithCancellation(cs.Token).Select(x => this.ProcessThisEmployee(x, cs.Token)).ToArray();
        Task.WaitAll(tasks);
    }
    catch (AggregateException ae)
    {
        // error handling code
    }
    // other stuff
}

private async Task ProcessThisEmployee(Employee x, CancellationToken token)
{
    ThirdPartyLibrary library = new ThirdPartyLibrary();
    token.ThrowIfCancellationRequested();

    using(token.Register(() => { library.Clean(); _countdownEvent.Signal(); })
    {
        await Task.Factory.StartNew(() => library.SomeAPI(x) );
    }
}

但是,您需要意识到,您尝试执行的操作可能很危险,因为您无法控制何时启动取消。如果您的一些员工在 using 块之前或之后并且您取消了操作 library.Clean 将不会被调用,因此您可以永远等待它。