它是为无锁设计一种合理的方法对于这种情况它是、这种情况、合理、方法

2023-09-07 16:44:26 作者:贱男春°

这是一种后续行动,我刚才的问题之一here.综上所述,我想拿出一个锁免费设计,这种情况下,我在取消任务我想打电话给第三方库的方法。在回答我的问题,一个有用的SO与会者建议使用CancellationToken.Register,但我不知道在哪里,我怎么可以用在这里。下面是code,我想出。请让我知道,如果你看到任何问题,这种方法或者有没有更好的方法来解决这个问题。

 类ProcessEmployees
    {
        私人列表<员工> _Employees;
        CancellationTokenSource CS =新CancellationTokenSource();

        公共ProcessEmployees()
        {
            _Employees =新的名单,其中,员工>()
            {
                新员工(){n = 1,名字=约翰,名字=李四},
                新员工(){n = 2,名字=彼得,名字=索尔},
                新员工(){n = 3,名字=迈克,名字=苏},
                新员工(){n = 4,名字=卡特琳娜,名字=Desoza},
                新员工(){n = 5,名字=保罗,姓氏=史密斯}
            };
        }

        公共无效StartProcessing()
        {
            尝试
            {
                任务[]任务= this._Employees.AsParallel()WithCancellation(cs.Token)。选择。(X => this.ProcessThisEmployee(X,cs.Token))的ToArray();
                Task.WaitAll(任务);
            }
            赶上(AggregateException AE)
            {
                //错误处理code
            }
            // 其他的东西
        }

        私人异步任务ProcessThisEmployee(员工X,的CancellationToken令牌)
        {
            ThirdPartyLibrary库=新ThirdPartyLibrary();
            如果(token.IsCancellationRequested)
                token.ThrowIfCancellationRequested();
            等待Task.Factory.StartNew(()=> library.SomeAPI(x)的);
            如果(token.IsCancellationRequested)
            {
                library.Clean();
                token.ThrowIfCancellationRequested();
            }
        }
    }
 

解决方案 一种安全的密码锁设计电路

您处理code可以简化为以下(这里是你如何使用注册)

 私人无效ProcessThisEmployee(员工X,的CancellationToken令牌)
{
    ThirdPartyLibrary库=新ThirdPartyLibrary();
    token.ThrowIfCancellationRequested();
    使用(token.Register(()=> library.Clean())
    {
        library.SomeAPI(X);
    }
    token.ThrowIfCancellationRequested(); //不知道为什么你取消在你这里原来的例子。
}
 

如果令牌被取消,而使用语句,它会调用的范围内 library.Clean()如果它被称为后它不会调用该函数。我也摆脱了你的 Task.Run 的,没有理由浪费额外的线程你在做什么。最后,我摆脱了多余的如果(token.IsCancellationRequested)检查, ThrowIfCancellationRequested()有,如果检查的内部本身,你不需要事先检查。

此外,因为在我的简化不必返回任务了您的StartProcessing code变为

 公共无效StartProcessing()
    {
        尝试
        {
            this._Employees.AsParallel()WithCancellation(cs.Token).ForAll。(X => this.ProcessThisEmployee(X,cs.Token));
        }
        赶上(AggregateException AE)
        {
            //错误处理code
        }
        // 其他的东西
    }
 

使用的ForAll(而不是选择(

This is kind of follow up to one of my earlier question here. In summary I am trying to come up with a lock free design for this scenario where I upon cancellation of task I want to call a method of third party library. In response to my question, a helpful SO participant suggested to use CancellationToken.Register but I am not sure where and how can I use that here. Below is code that I come up with. Please let me know if you see any issue with this approach or if there are any better alternatives to solve this problem.

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

解决方案

Your process code can be simplified to the following (here is how you use 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.
}

If the token is canceled while within the scope of the using statement it will call library.Clean() if it is called afterwards it does not call the function. I also got rid of your Task.Run, there is no reason to waste the extra thread for what you are doing. Lastly I got rid of the extra if (token.IsCancellationRequested) checks, ThrowIfCancellationRequested() has that if check inside of itself, you don't need to check beforehand.

Also, because in my simplification you are not returning tasks anymore your StartProcessing code becomes

    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
    }

using ForAll( instead of Select(