对于这种情况,无锁设计是否是一种合理的方法

本文关键字:一种 方法 是否是 情况 于这种 | 更新日期: 2023-09-27 18:37:08

这是我之前的一个问题的后续。总之,我正在尝试为这种情况提出一个无锁设计,在取消任务时,我想调用第三方库的方法。在回答我的问题时,一位乐于助人的SO参与者建议使用CancelToken.Register,但我不确定在哪里以及如何在这里使用它。下面是我想出的代码。如果您发现此方法有任何问题,或者是否有更好的替代方案来解决此问题,请告诉我。

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();
            if (token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();
            await Task.Factory.StartNew(() => library.SomeAPI(x) );
            if (token.IsCancellationRequested)
            {
                library.Clean();
                token.ThrowIfCancellationRequested();
            }
        }
    }

对于这种情况,无锁设计是否是一种合理的方法

您的流程代码可以简化为以下内容(以下是您使用 Register 的方法)

private void ProcessThisEmployee(Employee x, CancellationToken token)
{
    ThirdPartyLibrary library = new ThirdPartyLibrary();
    token.ThrowIfCancellationRequested();
    using(token.Register(() => library.Clean())
    {
        library.SomeAPI(x);
    }
    token.ThrowIfCancellationRequested(); //Not sure why you cancel here in your original example.
}

如果在 using 语句的范围内取消令牌,它将调用library.Clean()如果之后调用令牌,则不会调用该函数。我也摆脱了你的Task.Run,没有理由浪费额外的线程来做你正在做的事情。最后,我摆脱了额外的if (token.IsCancellationRequested)检查,ThrowIfCancellationRequested()有,如果检查本身,你不需要事先检查。

另外,因为在我的简化中,您不再返回任务,因此您的 StartProcessing 代码变成了

    public void StartProcessing()
    {
        try
        {
            this._Employees.AsParallel().WithCancellation(cs.Token).ForAll(x => this.ProcessThisEmployee(x, cs.Token));
        }
        catch (AggregateException ae)
        {
            // error handling code
        }
        // other stuff
    }

使用ForAll(而不是Select(