这是对我之前的一个问题here的跟进。总而言之,我正在尝试为这个场景设计一个无锁设计,在我取消任务时我想调用第三方库的方法。在回答我的问题时,一位有用的SO参与者建议使用CancellationToken.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();
}
}
}
答案 0 :(得分:2)
您的流程代码可以简化为以下(这是您使用注册的方式)
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(