在 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<Sites>(querySites).Result.ToList(); mo.PractitionerList = db.QueryAsync<Practitioners>(queryPractitioners).Result.ToList(); mo.ExistingAppointments = db.QueryAsync<AvailResponseObject>(queryExistingAppts).Result.ToList();
没有。 .Result
块。您必须使用await
来等待完成而不会阻塞。理查德的回答表明了这一点
【参考方案1】:
这比需要的复杂得多。将任务层封装到其他层通常不是正确的方法。
您最好将这三个查询作为异步操作执行(即它们返回Task<T>
,然后并行等待它们:
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 中使用任务并行库的主要内容,如果未能解决你的问题,请参考以下文章