【发布时间】:2018-06-04 13:11:33
【问题描述】:
我正在开发一个实用程序,它将使用 ExecuteNonQueryAsync 并行执行多个 SqlCommand。该实用程序应仅在所有请求完成后才返回,因此我使用的是 WaitAll。
事实上,这个实用程序是一个 SQL CLR 存储过程。 这排除了 Task.Run() 的使用。
我想捕获所有发生的个别异常,这似乎工作正常。我现在正在尝试添加获取数组中每个单独任务的已用持续时间的功能。
这里是代码的核心没有任何经过的时间信息:
// the commands array has been populated with an array of strings (TSQL statemements to execute)
Task<int>[] tasks = new Task<int>[commands.Length];
try {
for (int i = 0; i < commands.Length; i++) {
// executeSQL is an asyc method which makes the appropriate connection and returns await ExecuteNonQueryAsync
tasks[i] = executeSql(commands[i], connectionString, (int)commandTimeout);
}
Task.WaitAll(tasks);
} catch (AggregateException) {
// exceptions reported in GetResults
} finally {
//GetResults builds a datatable of { command, result, exception text }
var results = GetResults(commands, tasks);
}
现在,我正在努力弄清楚如何获取每个单独的 ExecuteNonQueryAsync 请求的经过时间。
我的第一个想法是创建一个秒表数组,并在executeSQL调用中添加一个秒表参数,然后将手表数组传递给GetResults函数:
Task<int>[] tasks = new Task<int>[commands.Length];
Stopwatch[] watches = new Stopwatch[commands.Length];
try {
for (int i = 0; i < commands.Length; i++) {
tasks[i] = executeSql(commands[i], connectionString, (int)commandTimeout, watches[i]);
}
Task.WaitAll(tasks);
} catch (AggregateException) {
// exceptions reported in finally
} finally {
var results = GetResults(commands, tasks, watches);
}
我可以在 executeSQL 函数中调用 ExecuteNonQueryAsync 之前启动秒表。但是我什么时候可以停止呢?
就我试图解决问题而言。我想我会添加一个延续来停止秒表,然后等待延续。但此代码无效(无法将类型“System.Runtime.CompilerServices.ConfiguredTaskAwaitable>”隐式转换为“System.Threading.Tasks.Task”)
var tasks = new Task<int>[commands.Length];
var watches = new System.Diagnostics.Stopwatch[commands.Length];
try {
for (int i = 0; i < commands.Length; i++) {
tasks[i] = executeSql(commands[i], connectionString, (int)commandTimeout, watches[i]).ContinueWith(
t => {
watches[i].Stop();
return t;
},
TaskContinuationOptions.AttachedToParent
).ConfigureAwait(false);
}
Task.WaitAll(tasks);
} catch (AggregateException) {
// ...
【问题讨论】:
-
将
Task分配给一个变量,ConfigureAwait在下一行的该变量上,然后将其添加到您的数组中。因为您已经将所有内容都写在了一行上,所以您正试图将ConfigureAwait的返回值添加到您的tasks数组中。 -
干杯科林。我发现试图确定该语言何时“神奇地”为您解决问题,以及何时不使用异步。我的意思是,一方面你所说的似乎非常明显,但另一方面,返回 Task
的异步函数实际上不能返回 Task ,而返回 Task 的异步函数可以'实际上并未分配给 Task 。例如: watch.Start(); Task<int> t = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); watch.Stop(); return t;无效!。 -
这是不可分配的,因为您正在等待
Task,然后尝试将其结果(即int)分配给Task<int>,而实际上它只是int,因为@ 987654333@ 已等待作业完成,因此打开了.Result对象。你真正想要的是watch.Start(); Task<int> t = cmd.ExecuteNonQueryAsync().ContinueWith(task => { watch.Stop() return task.Result; }); t.ConfigureAwait(false); return (await t);await不需要括号,但为了清楚起见添加了。 -
是的,我开始明白“魔法”语言实际上只存在于结果的任务化中。我仍然不太明白如何分配一个 int 作为结果,返回 int,然后语言会以某种方式计算出如果发生异常,这个 int 会以某种方式“携带”异常,因为它已被任务化。这真是令人困惑。但无论如何,虽然我认为你所拥有的会起作用,但我取得了相同的结果,但略有不同。立即添加答案
-
是的,我想我理解了它背后的想法:“等待”将任务解包到一个 int,这就是为什么你必须将返回一个 int 任务的函数的返回值分配给一个 int 而不是 int 的任务。但是,如果确实发生了异常,那么它本身异步的等待函数仍将返回一个任务,因此调用者可以使用异常。这对我来说有点道理,但状态机的魔力有点奇怪:) 谢谢你的帮助。
标签: c# asynchronous exception-handling stopwatch