Task.WaitAll, получить все исключения и длительности отдельных задач - PullRequest
0 голосов
/ 04 июня 2018

Я разрабатываю утилиту, которая будет выполнять несколько SqlCommands параллельно, используя ExecuteNonQueryAsync.Утилита должна возвращаться только после завершения всех запросов, поэтому я использую 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, а затем передать массив watches функции 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);
}

Я могу запустить секундомер непосредственно перед вызовом ExecuteNonQueryAsync внутри функции executeSQL.Но когда я смогу это остановить?

Это насколько я пытался решить проблему.Я думал, что добавлю продолжение, чтобы остановить секундомер и дождаться продолжения.Но этот код недопустим ( Не удалось неявно преобразовать тип '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) {
    // ...

1 Ответ

0 голосов
/ 04 июня 2018

Что ж, ответ состоял в том, что «простое» решение действительно работает, хотя я ожидал, что оно не будет.

Ниже приведено мое новое определение функции executeSQL и как она вызывается.

То, что причиняло боль моему мозгу, было обработкой исключений.Я подумал, что если сам язык превращает мои результаты в задачи и волшебным образом присоединяет исключения к этим результатам в задаче, когда это необходимо, то , конечно, , если я сам просто выбрасываю исключение обычным способом (согласноблок finally ниже) произойдет нечто неожиданное.(И тот факт, что ExecuteNonQueryAsync возвращает Задачу , но я должен присвоить результат int, а не Задаче - все еще очень странный для меня, и мне не нравится .)

Но нет!Кажется, это работает.

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) { 
    // ...
...