win*_*how 7 asp.net multithreading asynchronous task-parallel-library async-await
我有一个动作方法,需要根据用户选择的日期完成15~52个长时间运行的SQL查询(所有这些查询都相似,每个查询完成时间超过5秒).
在做了大量研究之后,似乎在不阻塞ASP.Net线程的情况下执行此操作的最佳方法是使用async/await任务方法使用SQL查询:
[HttpPost]
public async Task<JsonResult> Action() {
// initialization stuff
// create tasks to run async SQL queries
ConcurrentBag<Tuple<DateTime, List<long>>> weeklyObsIdBag =
new ConcurrentBag<Tuple<DateTime, List<long>>>();
Task[] taskList = new Task[reportDates.Count()];
int idx = 0;
foreach (var reportDate in reportDates) { //15 <= reportDates.Count() <= 52
var task = Task.Run(async () => {
using (var sioDbContext = new SioDbContext()) {
var historyEntryQueryable = sioDbContext.HistoryEntries
.AsNoTracking()
.AsQueryable<HistoryEntry>();
var obsIdList = await getObsIdListAsync(
historyEntryQueryable,
reportDate
);
weeklyObsIdBag.Add(new Tuple<DateTime,List<long>>(reportDate, obsIdList));
}
});
taskList[idx++] = task;
}
//await for all the tasks to complete
await Task.WhenAll(taskList);
// consume the results from long running SQL queries,
// which is stored in weeklyObsIdBag
}
private async Task<List<long>> getObsIdListAsync(
IQueryable<HistoryEntry> historyEntryQueryable,
DateTime reportDate
) {
//apply reportDate condition to historyEntryQueryable
//run async query
List<long> obsIdList = await historyEntryQueryable.Select(he => he.ObjectId)
.Distinct()
.ToListAsync()
.ConfigureAwait(false);
return obsIdList;
}
Run Code Online (Sandbox Code Playgroud)
进行此更改后,完成此操作所需的时间大大减少,因为现在我可以同时执行多个(15~52)异步SQL查询并等待它们完成而不是按顺序运行它们.但是,用户开始遇到大量的时间问题,例如:
(from Elmah error log)
"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool.
This may have occurred because all pooled connections were in use and max pool size was
reached."
"The wait operation timed out"
Run Code Online (Sandbox Code Playgroud)
它是由线程饥饿引起的吗?我得到了我可能会使用过多的线程的线程池,以达到我想要的感觉,但我认为它不应该是一个问题,因为我用异步/等待,以防止所有的线程被阻塞.
如果事情不会以这种方式工作,那么执行多个长时间运行的SQL查询的最佳做法是什么?
考虑限制正在执行的并发任务的数量,例如:
int concurrentTasksLimit = 5;
List<Task> taskList = new List<Task>();
foreach (var reportDate in reportDates) { //15 <= reportDates.Count() <= 52
var task = Task.Run(async () => {
using (var sioDbContext = new SioDbContext()) {
var historyEntryQueryable = sioDbContext.HistoryEntries
.AsNoTracking()
.AsQueryable<HistoryEntry>();
var obsIdList = await getObsIdListAsync(
historyEntryQueryable,
reportDate
);
weeklyObsIdBag.Add(new Tuple<DateTime,List<long>>(reportDate, obsIdList));
}
});
taskList.Add(task);
if (concurrentTasksLimit == taskList.Count)
{
await Task.WhenAll(taskList);
// before clearing the list, you should get the results and store in memory (e.g another list) for later usage...
taskList.Clear();
}
}
//await for all the remaining tasks to complete
if (taskList.Any())
await Task.WhenAll(taskList);
Run Code Online (Sandbox Code Playgroud)
请注意我已将您更改taskList为实际List<Task>,因为我们需要在列表中添加/删除任务,因此使用它似乎更容易.
此外,您应该在清除之前获得结果taskList,因为您将在以后使用它们.
| 归档时间: |
|
| 查看次数: |
575 次 |
| 最近记录: |