取消并行线程中的动作

时间:2017-08-16 12:41:49

标签: c# multithreading task-parallel-library cancellationtokensource

我发布的内容部分是关于任务并行库如何工作以及传播知识。此外,还要调查我的“取消”更新是否是导致用户突然退出的新问题的原因。

我正在开发的项目包含以下组件:

  • 网络表单网站。一个网站,作为管理公司车辆的门户。 进一步称为“Web”
  • WCF网络服务。单独机器上的后端服务。 进一步称为“服务”
  • 第三方服务。 进一步称为“第3”

注意:我使用的是.NET 4.0。因此,对任务并行库的较新更新不可用。

我被分配修复的问题是登录功能非常慢并且CPU密集。后来这被认为是第三方服务中的问题。但是我试图尽可能地优化登录行为。

登录请求和响应不包含特别多的数据。但是,为了收集响应数据,会对第三方服务进行几次API调用。

1。预先更改

Web 服务上调用WCF方法以收集“会话数据”。 这种方法有时需要很长时间才会超时(我认为超时设置为1分钟)。

“GetSessionData”方法的伪表示:

var agreements = getAgreements(request);
foreach (var agreement in agreements)
{
    getAgreementDetails(agreement);
    var customers = getCustomersWithAgreement(agreement);
    foreach (var customer in customers)
    {
        getCustomerInfo(customer);
        getCustomerAddress(customer);
        getCustomerBranches(customer);
    }
}

var person = getPerson(request);
var accounts = getAccount(person.Id);
foreach (var account in accounts)
{
    var accountDetail = getAccountDetail(account.Id);
    foreach (var vehicle in accountDetail.Vehicles)
    {
        getCurrentMilageReport(vehicle.Id);
    }
}
return sessionData;

请参阅gist了解代码段。

用户拥有的agreementsaccounts越多,此方法就会越来越重。

2。 Parallel.ForEach

我想我可以用foreach替换Parallel.ForEach()个循环。这大大提高了大用户的方法速度。

请参阅gist了解代码段。

3。取消

我们遇到的另一个问题是,当Web服务器服务器的CPU使用率最大化时,所有方法调用都会变慢,并且可能导致用户超时。并且对超时的流行响应是再次尝试,因此用户触发另一次由于CPU使用率高而“排队”(?)的登录尝试。这一切都在第一个请求尚未返回时。

我们发现如果网站超时,请求仍然存在。所以我们决定在 Service 方面实现类似的超时。

请参阅gist了解代码段。

我们的想法是使用CancellationToken调用GetSessionData(..),在与 Web 超时大约相同的时间后触发取消。如果没有人在那里展示或使用结果,那么就不会做任何工作。

我还实现了对第三方服务的方法调用的取消。

为所有循环和服务调用共享相同的CancellationToken是否正确?当抛出取消异常所有线程被“中止”时会出现问题吗?

请参阅gist了解代码段。

1 个答案:

答案 0 :(得分:3)

  

为所有循环和服务调用共享相同的CancellationToken是否正确?当所有线程都通过抛出取消异常而“中止”时会出现问题吗?

是的,这是正确的。是的,可能存在同时抛出大量异常的问题,但仅限于特定情况和大量并行工作。

一些提示:

  • 每个完整操作使用一个CancellationTokenSource。例如,每个请求。将相同的取消令牌从此源传递到每个异步方法
  • 您可以避免抛出异常并从方法返回。稍后,要检查工作是否已完成且未取消任何内容,您可以在cts上检查IsCancellationRequested
  • 在每次迭代的循环内检查令牌以取消,如果取消则返回
  • 仅在存在IO工作时使用线程,例如,当您从数据库查询某些内容或向其他服务请求时;不要将它用于CPU绑定工作

我在工作结束时感到很累,并提出了一件坏事。主要是,您不需要用于IO绑定工作的线程,例如,等待来自第三服务的数据库的响应。仅将线程用于CPU计算。

此外,我再次审核了您的代码,发现了几个瓶颈:

  1. 您可以异步调用GetAgreementDetail,GetFuelCards,GetServiceLevels,GetCustomers;不要等待每一个,而不是运行所有四个请求
  2. 您也可以并行调用GetAddressByCustomer和GetBranches
  3. 我注意到你使用了互斥锁。我想这是为了保护协议。客户和响应。客户加入。如果是这样,您可以减少锁定范围
  4. 您可以提前开始使用Vehicle,因为您知道方法开头的UserId;也可以并行执行