По-видимому, вы оказались правы в том, что await ничего не запускает, даже холодный таск. Такой код у меня вывел только Start. Хотя, может, я что-то и упустил. )
static async Task LoadAsync() => await new Task(() => System.Console.WriteLine("Load Async"));
static Task Load() => new Task(() => System.Console.WriteLine("Load"));
static async void Test()
{
System.Console.WriteLine("Start");
await LoadAsync();
await Load();
System.Console.WriteLine("Finish");
}
public static void Main()
{
Test();
var i = 0;
while (true)
{
i++;
}
}
По умолчанию принято считать что любой таск когда-нибудь выполнится если только обратное не написано в документации.
то для меня вдвойне очевидно, что загрузка выполнится асинхронно в случае работы с тасками, потому что никаких WaitAll я не делаю. А поскольку и в других местах используются подобные конструкции, например, с Close (где её убрать нельзя), то для общности мне хочется оставить её и с Load, путь даже это чуть менее оптимально с точки зрения компиляции.
Конечно, если вы видите более серьёзные потенциальные проблемы в виде блокировок, например, то поделитесь…
Знаете, почему вообще появились многие идеи из текущей статьи? Меня просто обескураживают некоторые детали «обычного паттерн-матчинга», а с помощью кастомных расширений я могу избежать их применения в своём коде, пусть даже ценой небольшого падения производительности (во многих случаях это для меня не критично).
Если бы мне в нём всё сразу понравилось, то никаких альтернатив точно бы не изобретал.
Последнее уточнение, для случая ...ForEach(async d => await d.Load())
компилятор сгененирует ощутимо менее оптимальный код, чем для ...ForEach(d => d.Load()), поэтому второй вариант предпочтительнее? Или дело в другом?
И для меня, в первую очередь, важна предсказуемость встроенных языковых средств, чтобы интуитивно заменив IModel на var где это, в привычном понимании, равноценная замена, не получить другое поведение программы.
Никак вам не различить эти ситуации. То, когда начнется выполнение, зависит от того, что внутри AnyTask, а не снаружи.
Если правильно понимаю вас, то при наличии интерфейса, как у меня, и абстрагируясь от конкретной имплементации документа, мне нужно явно указывать await у Load, чтобы гарантированно выполнить таск, верно? Или чего-то ещё не понимаю?
Хорошо, два случая var tasks = docs.Select(d => d.AnyTask()).ToArray();
docs.ForEach(async d => await d.AnyTask());
В первом случае хочу просто собрать таски и выполнить их потом. Во втором хочу начать выполнять немедленно. Как мне различить эти ситуации? В первом случае таски начнут выполняться сейчас же?
В моём случае с Load я не могу убрать await, поскольку метод возвращает таск, который мне нужно запустить. Если бы это был асинхронный воид метод, то тогда да, можно было бы написать так
то для меня вдвойне очевидно, что загрузка выполнится асинхронно в случае работы с тасками, потому что никаких WaitAll я не делаю. А поскольку и в других местах используются подобные конструкции, например, с Close (где её убрать нельзя), то для общности мне хочется оставить её и с Load, путь даже это чуть менее оптимально с точки зрения компиляции.
Конечно, если вы видите более серьёзные потенциальные проблемы в виде блокировок, например, то поделитесь…
async...awaitчеловек понимает, что с тасками идёт работа.В случае же
LoadAsync, можно подумать, что я вообще по старинке вручную создаю поток без всяких тасков.Всего лишь делюсь результатами.
Если бы мне в нём всё сразу понравилось, то никаких альтернатив точно бы не изобретал.
...ForEach(async d => await d.Load())мне срузу становится ясно, что загрузка идёт асинхронная, а вот
...ForEach(d => d.Load())ни о чём не говорит, нужно лезть в имплементацию или заранее именовать методы, как LoadAsync (при условии, что это мой код, а не чужой).
Из этих соображений читаемости для меня сейчас всё равно предпочтительным остаётся первый вариант…
Последнее уточнение, для случая
...ForEach(async d => await d.Load())компилятор сгененирует ощутимо менее оптимальный код, чем для
...ForEach(d => d.Load()), поэтому второй вариант предпочтительнее? Или дело в другом?И для меня, в первую очередь, важна предсказуемость встроенных языковых средств, чтобы интуитивно заменив IModel на var где это, в привычном понимании, равноценная замена, не получить другое поведение программы.
var tasks = docs.Select(d => d.AnyTask()).ToArray();docs.ForEach(async d => await d.AnyTask());
В первом случае хочу просто собрать таски и выполнить их потом. Во втором хочу начать выполнять немедленно. Как мне различить эти ситуации? В первом случае таски начнут выполняться сейчас же?
Насколько понимаю, если вместо StartNew буду использовать ReadToEndAsync, то тогда await убрать не смогу, верно? Или интуиция опять подводит?
async void Load() => await ......ForEach(d => d.Load());
async () => await SomeAsync()запускает таск, а вот() => SomeAsync()просто его возвращает, не запуская.Task task...
() => task = SomeAsync()
И как вообще я могу убрать await в таком случае?
Тасками я пользуюсь по большей части интуитивно и стараюсь избегать дебрей с контекстами синхронизации.