async await vs Результат от вызова более высокого уровня? - PullRequest
0 голосов
/ 29 сентября 2018

У меня есть такой простой метод репозитория:

public async Task<Blog> GetById(int id)
{
    return await _dbContext.Blogs.FirstOrDefaultAsync(p => p.Id == id);
}

Я прочитал некоторые ответы об асинхронном ожидании, но я все еще не понимаю, как правильно вызвать этот метод GetById:

public async Task DoSomething1()
{
    var blog = await _blogRepository.GetById(1);
    Console.WriteLine(blog.Title);
}

или

public void DoSomething2()
{
    var blog = _blogRepository.GetById(1).Result;
    Console.WriteLine(blog.Title);
}

Правильно означает: не блокируя поток, как описано в этом посте: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx?f=255&MSPPError=-2147217396

Лично я считаю, что правильным способом является DoSomething2 в этой ситуации.Поскольку блокировка потоков происходит во время работы FirstOrDefaultAsync, поэтому я использую async и await в методе GetById, поэтому действительно ли необходимо использовать еще один асинхронный await в более высоких методах, как в DoSomething1?можно ли использовать Result в этой ситуации, как в DoSomething2?

Каковы преимущества и недостатки выбора?

(я использую .NET 4.5)

Ответы [ 5 ]

0 голосов
/ 04 октября 2018

если к результату вызова метода, который возвращает результат задачи, применяется await, то типом выражения await будет Result.Если await применяется к результату вызова метода, который возвращает Task, тогда тип выражения await void

0 голосов
/ 04 октября 2018

Относительно первой реализации (DoSomething1) Вы можете прочитать здесь: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await

Выражение await не блокирует поток, в котором оно выполняется.Вместо этого он заставляет компилятор зарегистрировать оставшуюся часть асинхронного метода как продолжение ожидаемой задачи.Затем управление возвращается к вызывающей стороне асинхронного метода.Когда задача завершается, она вызывает ее продолжение, и выполнение асинхронного метода возобновляется с того места, где оно было остановлено.

Относительно секунды (DoSomething2) Согласно https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.result?redirectedfrom=MSDN&view=netframework-4.7.2#System_Threading_Tasks_Task_1_Result

Доступ к свойству get метода доступа блокирует вызывающий поток до завершения асинхронной операции;это эквивалентно вызову метода Wait.

Как только результат операции становится доступным, он сохраняется и сразу возвращается при последующих вызовах свойства Result.Обратите внимание: если во время выполнения задачи возникла исключительная ситуация или задача была отменена, свойство Result не возвращает значение.

Чтобы доказать это, вы можете проверить идентификаторы потока передвыполнение и после.Например,

var threadId1 = Thread.CurrentThread.ManagedThreadId;
var blogPost = await DoSomething1();
var threadId2 = Thread.CurrentThread.ManagedThreadId;
var blogPost = DoSomething2().Result;
var threadId3 = Thread.CurrentThread.ManagedThreadId;

Если вы выводите идентификаторы потоков, threadId2 и threadId3 в результате всегда будут одинаковыми, поэтому изменение потока не происходит и поток блокируется.

{
  "threadId1": 30,
  "threadId2": 15,
  "threadId3": 15
}
0 голосов
/ 29 сентября 2018

Task.Result вызов заблокирует вызывающий поток, пока операция не будет завершена.

Вот отличная статья объясняет это хорошо https://montemagno.com/c-sharp-developers-stop-calling-dot-result/

0 голосов
/ 03 октября 2018

Я знаю, что это спорная тема, и я думаю, что он заслуживает спорный ответ, так вот он идет:

1002 * Прежде всего , позвольте мне сделать это ясно, что DoSomething2 является блокирующимСпособ;это означает, что он будет работать в вызывающем потоке и блокировать вызывающий поток, в то время как DoSomething1 не будет блокировать вызывающий поток и вместо этого будет работать в потоке пула потоков.

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

Если ваше приложение предназначено для запускаединиц работы параллельно, тогда для меня имеет смысл использовать DoSomething1 вместо DoSomething2, поскольку вы могли бы максимально использовать свой ЦП, но если ваше приложение предназначено для последовательного выполнения единиц работы (какЯ полагаю, что вы делаете это в своем консольном приложении) вы не получите никакой выгоды от использования DoSomething1, поскольку одновременно будет выполняться только одна операция, и я бы вместо этого использовал DoSomething2.

СПри этом вы можете использовать AsyncContext.Run(() => MyMethodAsync(args)); в своем консольном приложении для запуска асинхронного метода в отдельном контексте.

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

PS вы должны исправить ошибку, упомянутую @ AydinAdn.

0 голосов
/ 29 сентября 2018

Это DoSomething1(), что позволит избежать блокировки, а не DoSomething2().

. Вы используете модификатор async, чтобы указать, что метод, лямбда-выражение или анонимный метод asynchronous

Асинхронный метод выполняется синхронно до тех пор, пока не достигнет своего первого выражения await, после чего метод приостанавливается до завершения ожидаемой задачи, в данном случае это будет асинхронный метод GetById.


У вас также есть ошибка в вашем коде ...

public async Task DoSomething1()
{
    var blog = await _blogRepository.GetById(1);
    Console.WriteLine(blog.Title); // this line has the bug
}

Метод GetById внутренне использует FirstOrDefault при поиске в основной базе данных, поэтому, если вы ищете блогесли объект не существует, метод вернет нулевой объект ... затем вы пытаетесь получить доступ к этим объектам, свойству Title, но из-за его нуля ... вы получите исключение нулевой ссылки.Проверьте, является ли объект нулевым, прежде чем пытаться получить доступ к его свойствам

...