如何在PLINQ中等待取消令牌寄存器方法

时间:2015-01-11 21:46:52

标签: c# .net-4.0 task plinq cancellation-token

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

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

1 个答案:

答案 0 :(得分:2)

好吧,您可以使用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) );
    }
}

但是,您需要意识到您尝试做的事情可能很危险,因为您无法控制何时启动取消。如果您的某些员工在使用区块之前或之后并取消操作,则不会调用library.Clean,因此您可以永久等待它。