- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我遇到的问题是对我正在执行的异步方法的调用是按顺序发生的。我正在将调用的任务添加到 ConcurrentBag 并等待包中的任务。我不关心这些调用的结果,我只需要确认它们已完成。但是,这些调用完全按顺序发生,这非常令人困惑。所讨论的方法通过带有参数化查询的 Npgsql 执行一些 PostgreSQL 查询。调用者获取我们自己的数据树并拉出树中的所有节点并遍历节点并在它们上执行此任务。我还使用自定义 AsyncHelper 类,它将迭代 IEnumerable 实现程序中的任务并等待其中的任务。我的 Tree 实现和 AsyncHelper 都在另一段代码中进行了测试,该代码执行与此代码相同的基本原理,它按预期异步执行任务。
我在函数调用上添加了日志记录,以确认这些调用是按顺序发生的。我事件还从包中取出方法并运行该方法,它仍然做同样的事情,它按顺序发生并且在完成之前不会继续我的循环。我所有的方法都标记为异步,直到循环结束后我才等待它们。
//method executing sequentially
public static async Task<List<ContactStatistic>> getContactStats(Guid tenantId, DateTime start, DateTime end, Breakdown breakdown) {
if (!await Postgres.warmConnection(5)) { return null; }
var hierarchy = await getTreeForTenant<TenantContactStatsNode>(tenantId);
//perform calculations to determine stats for each element
var calculationTasks = new ConcurrentBag<Task>();
var allData = await hierarchy.getAllData();
var timestampGotAllData = DateTime.Now;
foreach (var d in allData) {
calculationTasks.Add(d.getContactStats(start, end, breakdown));
}
Console.WriteLine("about to await all the tasks");
//await the tasks to complete for calculations
await AsyncHelper.waitAll(calculationTasks);
}
//method it's calling
public async Task getContactStats(DateTime start, DateTime end, Breakdown breakdown) {
//perform two async postgres calls
//await postgres calls
//validate PG response
//perform manipluation on this object with data from the queries
}
我希望第一个调用调用第二个函数,将任务添加到包中,并在完成后等待它们。实际发生的是该方法正在运行、完成,然后添加到包中。
* 编辑 *
下面是请求的第二次调用的完整代码。它根据时间从数据库中获取一些数据,填补拉回时间之间的空白,因此我们有一个完全顺序的返回列表,包括数据库中没有数据的所有时间,并将其放入对象级变量中
public async Task getContactStats(DateTime start, DateTime end, Breakdown breakdown) {
if (breakdown == Breakdown.Month) {
//max out month start day to include all for the initial month in the initial count
start = new DateTime(start.Year, start.Month, DateTime.DaysInMonth(start.Year, start.Month));
} else {
//day breakdown previous stats should start the day before given start day
start = start.AddDays(-1);
}
var tran = new PgTran();
var breakdownQuery = breakdown == Breakdown.Day ? Queries.GET_CONTACT_DAY_BREAKDOWN : Queries.GET_CONTACT_MONTH_BREAKDOWN;
tran.setQueries(Queries.GET_CONTACT_COUNT_BEFORE_DATE, breakdownQuery);
tran.setParams(new NpgsqlParameter("@tid", tenantId), new NpgsqlParameter("@start", start), new NpgsqlParameter("@end", end));
var tranResults = await Postgres.getAll<ContactDayStatistic>(tran);
//ensure transaction returns two query results
if (tranResults == null || tranResults.Count != 2) { return; }
//ensure valid past count was retrieved
var prevCountResult = tranResults[0];
if (prevCountResult == null || prevCountResult.Count != 1) { return; }
var prevStat = new ContactDayStatistic(start.Day, start.Month, start.Year, prevCountResult[0].count);
//ensure valid contact stat breakdown was retrieved
var statBreakdown = tranResults[1];
if (statBreakdown == null) { return;}
var datesInBreakdown = new List<DateTime?>();
//get all dates in the returned stats
foreach (var e in statBreakdown) {
var eventDate = new DateTime(e.year, e.month, e.day);
if (datesInBreakdown.Find(item => item == eventDate) == null)
datesInBreakdown.Add(eventDate);
}
//sort so they are sequential
datesInBreakdown.Sort();
//initialize timeline starting with initial breakdown
var fullTimeline = new List<ContactStatistic>();
//convert initial stat to the right type for final display
fullTimeline.Add(breakdown == Breakdown.Month ? new ContactStatistic(prevStat) : prevStat);
foreach (var d in datesInBreakdown) {
//null date is useless, won't occur, nullable date just for default value of null
if (d == null) { continue; }
var newDate = d.Value;
//fill gaps between last date given and this date
ContactStatistic.fillGaps(breakdown, newDate, prevStat.getDate(), prevStat.count, ref fullTimeline, false);
//get stat for this day
var stat = statBreakdown.Find(item => d == new DateTime(item.year, item.month, item.day));
if (stat == null) { continue; }
//add last total for a rolling total of count
stat.count += prevStat.count;
fullTimeline.Add(breakdown == Breakdown.Month ? new ContactStatistic(stat) : stat);
prevStat = stat;
}
//fill gaps between last date and end
ContactStatistic.fillGaps(breakdown, end, prevStat.getDate(), prevStat.count, ref fullTimeline, true);
//cast list to appropriate return type
contactStats.Clear();
contactStats = fullTimeline;
}
* 编辑 2 *下面是 AsyncHelper 用来等待这些任务的代码。此函数非常适用于使用同一框架的其他代码,它基本上只是清理必须等待枚举任务的代码。
public static async Task waitAll(IEnumerable<Task> coll) {
foreach (var taskToWait in coll) {
await taskToWait;
}
}
* 编辑 3 *根据建议,我将 waitAll() 更改为使用 Task.WhenAll() 而不是 foreach 循环,但是问题仍然存在。
public static async Task waitAll(IEnumerable<Task> coll) {
await Task.WhenAll(coll);
}
* 编辑 4 *为确保这不是 Postgres 调用造成的,我将第二种方法更改为仅执行打印行,然后休眠 200 毫秒以保持执行路径清晰。我仍然注意到这是完全按顺序发生的(甚至导致我对该函数的 POST 超时,因为实际的实际调用需要将近 20 毫秒)。下面是用于演示该更改的代码
public async Task getContactStats(DateTime start, DateTime end, Breakdown breakdown) {
Console.WriteLine("CALLED!");
Thread.Sleep(200);
}
* 编辑 5 *根据建议,我尝试了一个并行的 foreach 来尝试填充任务的 ConcurrentBag 而不是普通的 foreach。我在这里遇到一个问题,即并行 foreach 在第一次添加完成后完成,并且不会立即添加所有任务。
var calculationTasks = new ConcurrentBag<Task>();
var allData = await hierarchy.getAllData();
var timestampGotAllData = DateTime.Now;
Parallel.ForEach(allData, item => {
Console.WriteLine("trying parallel foreach");
calculationTasks.Add(item.getContactStats(start, end, breakdown));
});
Console.WriteLine("about to await all the tasks");
//await the tasks to complete for calculations
await AsyncHelper.waitAll(calculationTasks);
* 编辑 6 *为了视觉效果,我运行了代码并做了一些输出以显示发生的怪异情况。执行代码如下:
foreach (var d in allData) {
Console.WriteLine("Adding call to bag");
calculationTasks.Add(d.getContactStats(start, end, breakdown));
Console.WriteLine("Done adding call to bag");
}
输出是:https://i.imgur.com/3y5S4eS.png
因为它每次都打印“CALLED”,所以“Done!”在“完成添加对包的调用”之前,这些执行是按顺序发生的,而不是预期的异步。
最佳答案
我的直觉是,这与您在您的方法中打开的交易有关。很难确切地说出您的代码中发生了什么,因为这里似乎有一些自定义类 - 但是在您打开交易时是否可能正在进行一些锁定?由于这发生在您第一次等待之前,因此它必须在等待代码之前“按顺序”运行。
您的自定义“waitall”方法似乎不是问题所在,但您应该考虑删除它并使用内置的 Task.WhenAll 异步等待这些方法。
关于c# - 修复 C# 异步方法顺序执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55524761/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!