Async/Await VS Task.Run: Когда использовать? Как использовать?

Хорошо, надеюсь, что у меня есть основы асинхронного/ждущего, но все же некоторые вопросы задерживаются в моей голове.

Но теперь это проблема, о которой я говорю. Предположим в этом простом примере

static void Main(string[] args)
{

    Method();

    Console.WriteLine("Main Thread");

    Console.ReadLine();

}

public async static void Method()

{

    await Task.Run(new Action(LongTask));

    Console.WriteLine("New Thread");

}

public static void LongTask()

{

    Thread.Sleep(8000);

    Console.WriteLine("Long Task");

}

Основной поток продолжается и печатает Main Thread после вызова метода() и встречается с ожиданием в течение 8 секунд.

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

Сначала он печатает Main Thread.

Затем, после завершения 8 секунд, Long Task и затем New Thread печатаются.

Эта часть, которую я получил. Мой вопрос в моем приложении:

public IList<createcaseoutput> createCase(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput)    
{
    .............
    SQL.CaseSQL.getCreateCaseParameters(CreateCaseInput, out strSPQuery, out listParam);    
    var AcctLst = rep.ExecuteStoredProcedure<createcaseoutput>(strSPQuery, listParam).ToList();

    if (!string.IsNullOrEmpty(AcctLst.ElementAt(0).o_case_seq.ToString()))

    {
        await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);
    }

    console.writeline("Async called");
    return AcctLst;    
}

public async Task<ilist<savecasesearchoutput>> saveCaseSearch(SaveCaseSearchInput SaveCaseSearchInput,Int64? case_key)

{
    ..........................
    SQL.CaseSQL.getSaveCaseSearchParameters(SaveCaseSearchInput, case_key, out strSPQuery, out listParam);

    var AcctLst = await rep.ExecuteStoredProcedureAsync<entities.case.savecasesearchoutput>(strSPQuery, listParam);

    return AcctLst;
}

Здесь также createCase встречается ожидание и он должен немедленно вернуться прямо и выполнить самую следующую строку и напечатать Async called до того, как даже SaveCaseSearch завершится правильно?

Хорошо, если я думаю громко, это может быть control returns to the caller.

Точно так же, если я завершу свой вызов SavCaseSearch внутри другого метода async/await с именем предположим

async DoWork() {....
}

и назовите это DoWork() из CreateCase() напрямую, затем

It will go on printing "Async called" once call to DoWork() encounters await and before it even completes ?

Думаю ли я правильно?

Также иногда я вижу и путаюсь между

await someAsync() 

и

await Task.Run(() => someAsync())..

какая разница между ними? и какой из них следует?

Ответ 1

Мой вопрос в моем приложении:

Ваш код не будет компилироваться, потому что вы используете await без async. Исправлен код:

public async Task<IList<createcaseoutput>> createCaseAsync(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput)    
{
  ...
  await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);
  console.writeline("Async called");
  return AcctLst;    
}

Здесь также создается createCase, и он должен немедленно вернуться прямо и выполнить самую следующую строку и распечатать Async до того, как даже SaveCaseSearch завершится правильно?

Нет. Этот код:

  await saveCaseSearch(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);

совпадает с этим кодом:

  var saveTask = saveCaseSearchAsync(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);
  await saveTask;

Итак, во-первых, createCaseAsync вызовет saveCaseSearchAsync. Предположительно, saveCaseSearchAsync выполняет некоторую асинхронную операцию, поэтому она вернет неполную задачу в createCaseAsync. createCaseAsync, затем await эта задача, которая заставляет ее возвращать неполную задачу своему вызывающему.

В конце концов, завершится saveCaseSearchAsync, который завершит возвращенную задачу (которую я назвал saveTask в приведенном выше коде). Это, в свою очередь, будет продолжать выполнение createCaseAsync, и оно перейдет к следующей строке и выведет на консоль "Async".

Так что, если я завершу свой вызов SavCaseSearch внутри другого метода async/await

Вам не нужна оболочка, потому что createCaseAsync уже возвращает Task.

какая разница между ними? и какой из них следует?

Task.Run в основном предназначен для отталкивания блокировки от потока пользовательского интерфейса и от потока. Поскольку вы находитесь на ASP.NET, не используйте Task.Run.

Ответ 2

Первое правило async - всегда использовать async или никогда не использовать async.

Если ваш базовый API не может обрабатывать async, использовать async в верхних слоях (например, ASP.NET MVC) бесполезно, поскольку в какой-то момент вы получите головоломку потока, поскольку все потоки заняты ожиданием операций ввода-вывода ( как вызовы БД).

Ваш пример - это классический случай, когда вы смешиваете синхронизацию и асинхронный режим. Вызов Sleep блокирует поток до его завершения. Вы должны были использовать Task.Delay, так как он выпустил бы поток до тех пор, пока не завершится задержка.

Мой совет для вас - просто начать, следуя правилу, которое я упомянул первым, и только когда задействуются операции привязки IO, такие как DB или вызовы файлов. Затем, когда вы лучше поймете async, вы можете начать разорвать его, так как у вас есть гораздо лучшее понимание того, к чему это может привести.

(Извините, что не отвечал на ваши вопросы напрямую, но нарезка - это сложная тема, и ваш мозг может поджарить, если вы попытаетесь получить все это напрямую. Начните с малого.)

Ответ 3

Относительно разницы между асинхронными/ожидающими и задачами...

Async/Await являются синтаксическими ключевыми словами, чтобы упростить ваш код, поскольку все до ключевого слова await происходит в вызывающем потоке, и все, что происходит от ожидания, происходит в продолжении задачи.

Настройка этого с помощью задач с использованием TPL потребует много кода и удобочитаемости. Однако обратите внимание, что под ним все еще используются задачи и продолжения.

Кроме того, они не всегда могут использоваться вместо Заданий, например, когда завершение задачи является недетерминированным или если у вас есть несколько уровней задач вместе с использованием TaskCompletionSource.

Для получения дополнительной информации прочтите главу 4 "Асинхронное программирование" в книге "" Написание высокопроизводительного .NET-кода" от Ben Watson

Примечание. Кроме того, TPL использует пул потоков .NET, но делает это более разумно, выполняя несколько задач в одном потоке последовательно, прежде чем возвращать поток обратно в пул. Это можно сделать с помощью интеллектуального использования объектов-делегатов.

Ответ 4

Здесь также создается createCase, и он должен немедленно вернуться прямо и выполнить самую следующую строку и распечатать Async до того, как даже SaveCaseSearch завершится правильно?

Это не должно даже компилироваться. Оператор "ожидание" может использоваться только в методе "асинхронный". Тем не менее, если вы удалите оператор "ожидание" , следующая строка будет печатать "Async called" до завершения saveCaseSearch.

Думаю ли я правильно?

saveCaseSearch уже является методом async, поэтому вам не нужно обертывать его для достижения желаемого результата. Тем не менее, вы можете обернуть его другим способом, если вы действительно этого хотите.

какая разница между ними? и какой из них следует?

Оператор "ожидание" ждет объекта "Задача", так что любой из них в порядке. Я бы выбрал await someAsync(), потому что это меньше кода для записи.