在 API 中使用任务并行库

Posted

技术标签:

【中文标题】在 API 中使用任务并行库【英文标题】:Using Task Parallel Library in API 【发布时间】:2021-06-23 08:42:08 【问题描述】:

我有一个方法,我想并行运行三个查询。我正在尝试使用 Task Parallel 库来执行此操作,如下所示:

        internal static async Task<RequestAppointmentDBMasterObject> GetDAMasterObject(string facility, string sites, string state)
        
            
            RequestAppointmentDBMasterObject mo = new RequestAppointmentDBMasterObject();

            string querySites = query1;
            string queryPractitioners = query2;
            string queryExistingAppts = query3;

        using (IDbConnection db = new OdbcConnection(connectionString))
        
            await Task.Run(() =>
            
                Parallel.Invoke(
                    () =>  mo.SiteList = db.Query<Sites>(querySites).ToList(); ,
                    () =>  mo.PractitionerList = db.Query<Practitioners>(queryPractitioners).ToList(); ,
                    () =>  mo.ExistingAppointments = db.Query<AvailResponseObject>(queryExistingAppts).ToList(); 
                );
            );
        

            return mo;
        

然后,在另一个类上,我调用此方法来设置我要使用的对象,如下所示:

        RequestAppointmentDBMasterObject mo = DataAccess.GetDAMasterObject(facility, site, state).Result;

问题是,当我调用 API 时,我从来没有得到返回结果。一旦我将所有内容改回不使用任务和并行库。一切正常。 我还在控制台应用程序中尝试了相同的代码,并获得了数据。我不确定为什么我可能会丢失代码,所以我可以在我的 API 上使用它。 提前谢谢你。

【问题讨论】:

此代码包含几个错误。 Parallel.Invoke 根本没有帮助。 Dapper 有 QuerryAsync,所以没有理由调用 Task.Run 调用 Query. Worse, Parallel.Invoke` 正在修改共享状态 @PanagiotisKanavos,感谢您的反馈。我刚刚修复了调用 Parllel.Invoke 的方式。你能发布一个关于如何使用 QueryAsync 执行此操作的示例 不管你怎么称呼Parallel.Invoke。首先使用它是错误的。它在浪费线程并且阻止你使用QueryAsync @PanagiotisKanavos,说这将并行执行是否有效? mo.SiteList = db.QueryAsync&lt;Sites&gt;(querySites).Result.ToList(); mo.PractitionerList = db.QueryAsync&lt;Practitioners&gt;(queryPractitioners).Result.ToList(); mo.ExistingAppointments = db.QueryAsync&lt;AvailResponseObject&gt;(queryExistingAppts).Result.ToList(); 没有。 .Result 块。您必须使用await 来等待完成而不会阻塞。理查德的回答表明了这一点 【参考方案1】:

这比需要的复杂得多。将任务层封装到其他层通常不是正确的方法。

您最好将这三个查询作为异步操作执行(即它们返回Task&lt;T&gt;,然后并行等待它们:

var t1 = db.QueryAsync(...);
var t2 = db.QueryAsync(...);
var t3 = db.QueryAsync(...);

await Task.WhenAll(t1, t2, t3);

这确实取决于支持多个并发连接的数据库连接(这通常不是真的)。​​

使用t1.Result等获取返回值。

【讨论】:

感谢您的反馈@Richard。请注意,我更新了问题以显示我如何使用 Parallel.Invoke。你能发布一个例子来说明你的意思吗 刚刚应用这个并且工作。已经看到了更好的表现。非常感谢您的反馈。 @TheodorZoulias 哎呀,忘了解决这个问题。正在考虑其他Parallel 方法。【参考方案2】:

如 cmets 中所述,您根本不想使用 Parallel。相反,请使用异步并发 (await Task.WhenAll)。

但是,大多数数据库提供程序不允许多个同时请求通过同一数据库连接。所以您可能需要三个不同的数据库连接才能同时运行三个查询。

internal static async Task<RequestAppointmentDBMasterObject> GetDAMasterObject(string facility, string sites, string state)

  RequestAppointmentDBMasterObject mo = new RequestAppointmentDBMasterObject();
  string querySites = query1;
  string queryPractitioners = query2;
  string queryExistingAppts = query3;

  using (IDbConnection db1 = new OdbcConnection(connectionString))
  using (IDbConnection db2 = new OdbcConnection(connectionString))
  using (IDbConnection db3 = new OdbcConnection(connectionString))
  
    var task1 = db1.Query<Sites>(querySites).ToListAsync();
    var task2 = db2.Query<Practitioners>(queryPractitioners).ToListAsync();
    var task3 = db3.Query<AvailResponseObject>(queryExistingAppts).ToListAsync();
    await Task.WhenAll(task1, task2, task3);
    mo.SiteList = await task1;
    mo.PractitionerList = await task2;
    mo.ExistingAppointments = await task3;
  
  return mo;

像这样执行多个并发查询是可能的,但通常最好执行一次单个查询来一次全部恢复。这样你只需要一个db连接,所有的查询结果都是一致的。

【讨论】:

感谢您的反馈。这澄清了我的一个问题“大多数数据库提供商不允许多个同时请求”。我喜欢你展示的方法。 似乎这个数据库支持并发连接,因为理查德的建议奏效了。我会记住这一点以供将来参考。谢谢斯蒂芬。 是否有理由执行await Task.WhenAll(task1, task2, task3) 后跟三行再次等待每个任务? @Enigmativity:只是偏好。我更喜欢单个 await Task.WhenAll 以使代码尽可能清晰。 很公平。我想知道这是否是个人编码偏好。很清楚。

以上是关于在 API 中使用任务并行库的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 C# 2.0 中使用任务并行库 (TPL)?

我可以在 .Net 3.5 项目中使用任务并行库吗?

使用任务并行库的更好方法是啥

多线程编程学习笔记——任务并行库

使用任务并行库 (TPL) 进行轮询

有没有办法将任务并行库(TPL)与 SQLDataReader 一起使用?