并行或异步ASP.NET核心C#

并行或异步ASP.NET核心C#,c#,asynchronous,asp.net-core,parallel.foreach,C#,Asynchronous,Asp.net Core,Parallel.foreach,我在谷歌上搜索了很多,但恐怕我不完全理解并发和并行的后果 我有大约3000行数据库对象,每个数据库对象平均附加了2-4个逻辑数据,需要作为搜索查询的一部分进行验证,这意味着验证服务需要执行大约3*3000次。例如,用户已对颜色进行过滤,然后每行需要验证颜色并返回结果。当找到匹配项时,循环无法中断,这意味着所有逻辑对象将始终需要求值(这是由于相关性的计算,而不是匹配项) 这是在用户选择各种属性时按需完成的,这意味着性能在这里是关键 我目前正在使用Parallel.ForEach来实现这一点,但我想

我在谷歌上搜索了很多,但恐怕我不完全理解并发和并行的后果

我有大约3000行数据库对象,每个数据库对象平均附加了2-4个逻辑数据,需要作为搜索查询的一部分进行验证,这意味着验证服务需要执行大约3*3000次。例如,用户已对颜色进行过滤,然后每行需要验证颜色并返回结果。当找到匹配项时,循环无法中断,这意味着所有逻辑对象将始终需要求值(这是由于相关性的计算,而不是匹配项)

这是在用户选择各种属性时按需完成的,这意味着性能在这里是关键

我目前正在使用Parallel.ForEach来实现这一点,但我想知道使用异步行为是否更明智

当前方式

var validatorService=new LogicalGroupValidatorService();
ConcurrentBag结果=新ConcurrentBag();
Parallel.ForEach(searchGroups,(group)=>
{
var searchGroupResult=validatorService.ValidateLogicGroupRecursivly(
propertySearchQuery,group.StandardPropertyLogicGroup);
添加(新的StandardSearchResult(searchGroupResult));
});
异步示例代码

var validatorService=new LogicalGroupValidatorService();
列表结果=新列表();
var tasks=新列表();
foreach(searchGroups中的var组)
{
tasks.Add(validatorService.ValidateLogicGroupRecursivlyAsync(
propertySearchQuery,group.StandardPropertyLogicGroup);
}
等待任务。何时(任务);
结果=任务。选择(logicalGroupResultTask=>
新的StandardSearchResult(logicalGroupResultTask.Result)).ToList();

并行和异步的区别在于:

  • 并行:旋转多个线程并将工作分配给每个线程
  • 异步:以非阻塞方式执行工作
这是否有区别取决于以异步方式阻塞的内容。如果你在CPU上工作,是CPU阻塞了你,因此你仍然会有多个线程。如果是IO(或者CPU之外的任何东西,您将重用同一线程)

对于您的特定示例,这意味着:

Parallel.ForEach
=>为列表中的每个项目启动新线程(启动的线程数由CLR管理),并在不同的线程上执行每个项目

async/await
=>完成这部分工作,但让我继续执行。因为你有很多东西,这就意味着你要说多次。现在要看结果如何:

  • 如果
    这部分工作
    在CPU上,效果相同
  • 否则,当工作在其他地方完成时,您将只使用一个线程

既然您似乎已经实现了这两个版本,那么如果您衡量这两个版本的执行时间,会是什么样子?您衡量了性能上的差异吗?这是唯一可以确定的方法。这就是说,我的猜测是,在这种情况下,并行应该表现得更好,因为异步主要是“在等待其他系统时不要阻塞主线程”,那么ValidateLogicGroupRecursive是在数据库中工作,还是一切都在内存中完成?对于3000或9000行,这并不重要,除非您要对数据库进行一些往返或每行执行任何CPU繁重的工作,否则这并不重要。9000次循环迭代来计算一些简单的公式或值对于今天的计算机来说已经不算什么了。除非你已经在它上面测量了现实世界的瓶颈问题,否则就在一个线程上进行。在ASP.NET Core中启动/排队太多线程实际上可能会降低总体性能,而不是提高性能(ASP.NET Core在高流量情况下,当线程(排队)用完时会拒绝连接),如果您必须对DB进行任何往返,则可能有更好的方法来解决此问题(在代码中找出所需的值,然后在单个查询中提取所有值,然后在本地执行计算)(高CPU意味着每行1-2毫秒,因此整个计算将花费9到18秒。今天的CPU上的典型计算范围为ns,因此9000条记录在1或2毫秒之间没有太大区别)根据其他评论(启动3000个任务不是最明智的做法),在Task.Run()中包装一个标准foreach(取代Parallel.foreach)会更好吗然后等待该任务?如果我理解正确,这意味着每9000次迭代请求只创建一个任务。@Max:这样做没有任何好处。您只需释放请求线程并使用池中的另一个线程。因此线程使用与在请求线程上执行相同,只是您得到了一点线程池管理在ASP.NET内核请求上执行CPU限制的工作是非常好的thread@Tseng,因此本质上,它可以简单到:
var results=searchGroups.Select(group=>newstandardseachresult(validatorService.ValidateLogicGroupRecursivly(propertySearchQuery,group.standardpropertyLogicGroup))。ToList()
并对它感到满意吗?@Max这里的关键是衡量性能。这不仅意味着为单个用户运行时的性能,还意味着如果需要同时为多个用户运行时会发生什么。对于一个用户,并行运行可能会更快,对于多个用户,您可能会得到不同的result(取决于CPU的使用量)。这有点误导,因为1)并行。ForEach
不启动新线程,它使用线程池中的任务,2)调用
异步/await
on
任务。Whalll
不可能使用单个线程,相反,
任务。Whalll(任务)
将始终创建
任务。计数
任务。