Task.WaitAll,获取所有异常和单个任务工期



我正在开发一个实用程序,它将使用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) {
// ...

好吧,答案是"简单"的解决方案确实有效,尽管我预计它不会。

下面是我对executeSQL函数的新定义,以及如何调用它。

让我脑子受伤的一点是异常处理。我想,如果语言本身将我的结果变成任务,并在需要时神奇地将这些结果附加到任务中,那么,当然,如果我只是以正常方式自己抛出异常(如下面的最终块(,就会发生意想不到的事情。(事实上,ExecuteNonQueryAsync 返回一个 Task,但我必须将结果分配给一个 int - 而不是 Task- 对我来说仍然很奇怪,我不喜欢它。

但是没有!它似乎只是有效。

static async internal Task<int> executeSql(string tsql, string connectionString, int commandTimeout, System.Diagnostics.Stopwatch watch) {
int i = -1;
Exception ex = null;
try {
using (var con = new SqlConnection(connectionString)) {
con.Open();
using (var cmd = new SqlCommand("set xact_abort on;" + tsql, con)) {
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = commandTimeout;
watch.Start();
i = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
}
}
} catch (Exception e) {
ex = e;
} finally {
watch.Stop();
if (ex != null) { throw ex; }
}
return i;
}
// ....
var tasks = new Task<int>[commands.Length];
var watches = new System.Diagnostics.Stopwatch[commands.Length];
try {
for (int i = 0; i < commands.Length; i++) {
watches[i] = new System.Diagnostics.Stopwatch();
tasks[i] = executeSql(commands[i], connectionString, (int)commandTimeout, watches[i]);
}
Task.WaitAll(tasks);
} catch (AggregateException) { 
// ...

最新更新