Насколько правильно или не правильно делать ожидание Funcfunc при вызове await func.Invoke(string,string); в C# и почему?
Насколько правильно или не правильно делать ожидание Func<String,String,Task>func при вызове await func.Invoke(string,string); в C# и почему? А так же как вызовется этот метод в итоге? Синхронно или асинхронно?
public class A
{
public Func<string, string, Task> ResiveMassage;
public void Start()
{
B b = new B(ResiveMassage);
while (true)
{
_ = Task.Run(async () => await b.Resive()).ConfigureAwait(false);
}
}
}
public class B
{
string h = "Hello";
string w = "World";
Func<String, String, Task> ResiveMassage;
public B(Func<String, String, Task> resiveMassage)
{
ResiveMassage = resiveMassage;
}
public async Task Resive()
{
await ResiveMassage?.Invoke(h,w);
// await Task.Delay(5000);
}
}
public class C
{
public void Strat()
{
A a = new A();
a.ResiveMassage += ResiveMessageAsync;
Thread thread = new Thread(new ThreadStart(()=>a.Start()));
thread.Start();
}
async Task ResiveMessageAsync(string h, string w)
{
await Task.Run(() => Console.WriteLine($"{h} {w}"));
// await Task.Delay(5000);
}
}
class Program
{
static void Main(string[] args)
{
C c = new C();
c.Strat();
Console.ReadLine();
}
}
Ответы (1 шт):
Совершенно неважно, вызываете ли вы делегат или просто метод. И то и другое может завершиться синхронно, например.
Здесь даже мудрить с классами не надо, чтобы понять.
static async Task Main(string[] args)
{
await DoA();
await DoB();
await DoC();
Console.ReadLine();
}
private static async Task DoA()
{
Console.WriteLine("Hello World");
}
private static async Task DoB()
{
Console.WriteLine("Hello World");
await Task.Delay(1000);
}
private static Task DoC()
{
Console.WriteLine("Hello World");
return Task.Delay(1000);
}
Здесь метод DoA() выполнится синхронно, остальные асинхронно. Но Console.WriteLine() везде выполнятся синхронно. Подробнее как это работает можно узнать здесь.
Так же популярная ошибка начинающих разработчиков - путать асинхронность и многопоточность. Это вообще не похожие друг на друга вещи. Первое - это организация кода так, чтобы он продолжал выполняться по завершению какой-то операции. Второе - много потоков. В асинхронной операции может вообще не использоваться дополнительных потоков.
Что делает await Task.Run:
- Спавнит новый поток или берет из пула потоков существующий, если уже есть такой
- Запускает в этом потоке код
- Возвращает
Task, дающий возможность асинхронно дождаться конца выполнения кода в другом потоке
То есть здесь присутствует и асинхронность и многопоточность, и выполняют они совершенно разные задачи. Многопоточность вам позволяет запустить CPU-зависимый код не занимая текущий поток, а асинхронность дождаться конца его выполнения. Если бы асинхронность не использовалась, как таковая, использовался бы либо колбэк, что и есть сама суть асинхронности на низком уровне, либо просто Sleep, что вообще костыль и бессмысленно.
Нет смысла выполнять код в другом потоке, когда текущий блокируется на время выполнения. Ну потому что для выполнения куска синхронного кода достаточно одного потока, все что больше - излишество. За исключением конечно же ситуаций, когда вам реально нужны распеделенные вычисления. Тогда многопоточность к вашим услугам.
Далее, нужно понять, почему await без async нельзя, а наоборот - можно. Потому что первый генерирует машину состояний, а второй ее использует. Сгенерить машину состояний и не использовать ее конечно можно (метод DoA), но какой в этом смысл? Смысл в машине состояний появляется тогда, когда у нее существует более двух состояний. В противном случае достаточно либо обычного ветвления if-else, либо вообще код будет линейный.
Насколько правильно или не правильно делать ожидание
Func<String,String,Task>funcпри вызовеawait func.Invoke(string,string)в C# и почему?
Этот вопрос по своей сути поставлен неправильно. Все зависит от реализации самого метода. Если метод совсем не ожидать, то зачем в принципе асинхронность нужна? Как я чуть выше писал, вся суть в асинхронности в ожидании await. Нет await - нет асинхронности, ну только если вы не обвешаетесь колбэками или континуациями .ContinueWith(). Ведь поведение await можно эмулировать с помощью написания самого обычного кода. async/await по своей сути - синтаксический "сахар", позволяющий писать с виду линейный код, когда внутри по своей сути он таковым не является. Для новичка выглядит как магия, но это до тех пор, пока он не закончит путать асинхронность с многопоточностью.
А так же как вызовется этот метод в итоге? Синхронно или асинхронно?
Вызовется или выполнится (завершится)?
- Если "вызовется", то все методы вызываются синхронно, и вызвать метод асинхронно технически невозможно.
- Если речь про "завершится", то совершенно неважно, что там этот метод возвращает, главное чтобы возвращал
Task- первое условие асинхронности. У васTaskвозвращается, это говорит о том, что метод может завершиться асинхронно, а может и синхронно. Надо смотреть, что там внутри, смотрим:await Task.Run(() => ...и больше ничего, ноawaitесть, он возвращает тот самый заветныйTask. Логичный вопрос: а методTask.Run()может завершиться асинхронно, там естьawaitвнутри? Грубо говоря "есть", но "нет", там есть специальный "ожидатель", код, который исходя из того, что там с запущенным потоком происходит, меняет внутреннее состояние того самогоTask, который он вам вернул. Класс, управляющий состояниемTask, называетсяTaskCompletionSource. Вот весьма хорошее объяснение, как это работает.
Так как все-таки, синхронно или асинхронно?
Асинхронно, но это не точно. Потому что есть Task и есть await. А почему не точно? А потому что за запуск тасок отвечает TaskScheduler (ссылка .NET Blog), а он может вообще быть синхронным, это уж как вы пожелаете.
Асинхронное программирование - это просто. :)