Должен ли я обернуть задачу в другую задачу или просто вернуть созданную задачу?

Я создаю приложение .NET 4.0, использующее ADO.NET, поэтому я не могу использовать async/await. Мне не нужно решение для этого, но я хочу знать, какая из следующих реализаций лучше и почему. Мои модульные тесты проходят для всех трех реализаций, но я хочу знать разницу между этими тремя.

#1 Вложенные задачи

В моей первой реализации я оборачиваю задачу в другую задачу. Я думаю, что раскручивание двух задач плохо влияет на производительность, но я не уверен.

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    return Task.Factory.StartNew(() =>
    {
        var sqlCommand = CheckIfSqlCommand(dbCommand);
        PrepareExecuteReader(dbCommand);

        return Task<IDataReader>
            .Factory
            .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
            .Result;
    }, cancellationToken);
}

# 2 Использование TaskCompletionSource

Затем я попытался обернуть результат в TaskCompletionSource, чтобы у меня была только одна задача.

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    var taskCompletionSource = new TaskCompletionSource<IDataReader>();
    var sqlCommand = CheckIfSqlCommand(dbCommand);
    PrepareExecuteReader(dbCommand);

    var reader = Task<IDataReader>
        .Factory
        .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null)
        .Result;

    taskCompletionSource.SetResult(reader);

    return taskCompletionSource.Task;
}

# 3 возвращает Task напрямую

Мое окончательное решение состоит в том, чтобы напрямую вернуть задачу, которую я создал, а не обертывать ее.

public virtual Task<IDataReader> ExecuteReaderAsync(IDbCommand dbCommand, CancellationToken cancellationToken)
{
    var sqlCommand = CheckIfSqlCommand(dbCommand);
    PrepareExecuteReader(dbCommand);

    return Task<IDataReader>
        .Factory
        .FromAsync(sqlCommand.BeginExecuteReader, sqlCommand.EndExecuteReader, null);
}

Итак, в основном мой вопрос:

Какой вариант я должен использовать или есть лучший способ сделать это?


person annemartijn    schedule 27.02.2014    source источник
comment
Ориентация на .Net 4.0 не обязательно означает, что вы не можете использовать async-await. Вы можете использовать его, если добавите Microsoft.Bcl.Async в свой проект, хотя для этого требуется VS 2012.   -  person svick    schedule 27.02.2014
comment
Разработчики также должны иметь возможность использовать vs2010, но я ценю ваши отзывы.   -  person annemartijn    schedule 28.02.2014


Ответы (2)


Ваш номер 3 лучший. Первые два вносят осложнение без причины.

1 потенциально добавляет еще один поток исключительно для асинхронного запуска CheckIfSqlCommand() и PrepareExecuteReader(). Это может быть то, что вы хотели, но они не звучат как команды, которые займут много времени.

2 ссылается на .Result задачи, которая будет блокироваться до тех пор, пока задача не будет завершена, что сводит на нет всю цель использования задач.

person GazTheDestroyer    schedule 27.02.2014
comment
Интересно, бывают ли ситуации, когда вы не используете задачи для ввода-вывода или работы в сети. Если да, то как узнать, когда использовать асинхронность? - person annemartijn; 27.02.2014
comment
@annemartijn, если вам нужно спросить, не используйте асинхронность. Используйте его, когда необходимо. - person usr; 27.02.2014

Есть две сцены, в которых мы используем асинхронное программирование с Tasks: одна — массовые вычисления, другая — I/O.
В ситуации массовых вычислений мы всегда используем Task.Run, чтобы запросить поток из пула потоков, чтобы избежать блокировки потока.
В В I/O ситуации, если async API не предоставляется, мы всегда используем TaskCompletionSource или Task.Factory.FromAsync для создания метода async. Я думаю, что смешивать эти два — не очень хорошее решение.
Кстати, Task.Run всегда используется в клиентском приложении, серверная часть обычно не используется Task.Run из-за параллельного запроса.
Вот хороший пост, который вы можете см.:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2010/september/async-tasks-simplify-asynchronous-programming-with-tasks

person 西红柿炒恐龙蛋    schedule 04.03.2021