Обновить
58

Пользователь

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

Вы делайте, как хотите, а я лучше сразу обнаружу проблему и исправлю.
Это не смешная шутка, даже я со своим далеко не самым глубоким знанием тасков прекрасно понимаю разницу. Если метод асинхронный, то подобные блокирующие вызовы в нём нужно оборачивать в таски, иначе толку от асинхронности метода никакой.
Чтобы уж наверняка, я переименовал свои методы как Foreach1, после чего следующие вариации скомпилировались и заработали асинхронно и параллельно

Documents.ForEach1(d => d.Expose())
.ToList().ForEach(async d => await d.Load());

Documents.ForEach1(d => d.Expose())
.ToList().ForEach(d => d.Load());


Асинхронность отслеживаю визуально по состоянию прогресс-баров. Задержки добавляю случайно в классе PlainTextDocument

private static Random rand = new Random();
private async Task<bool> AsyncLoad()
{
	await Task.Delay(3000 + rand.Next()%10000);
	return (Model = _originalModel = (await GetKey()).Is(out var key)
		? await Wrap.Storage.LoadText(key, EncodingModel.Encoding)
		: null).Put(key.Is());
}


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

Кстати, на практике асинхронно и параллельно у меня работают и все вариации с ForEach (c async и без, у List и как своё расширение).
Понял вас. На самом деле, идея расширения более простая. Это, во-первых, замена обычно цикла foreach для коллекций на метод, как у List'а, а, во-вторых, возможность вернуть коллекцию дальше в цепочку вызовов.

Конкретно таски она затрагивает лишь косвенно. С таким же успехом я мог бы использовать стандартный метод класса List и писать в нём async...await.

Я учёл ваши замечания насчёт Load и переписал через WhenAll. Теперь всё работает параллельно и асинхронно (при добавлении рандомных задержек в метод, прогресс-бары скрываются в разные моменты времени для каждого документа). Одобряете такой код?
Конечно, мне интересно узнать, какие ещё могут варианты произойти.
По-видимому, вы оказались правы в том, что 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, путь даже это чуть менее оптимально с точки зрения компиляции.

Конечно, если вы видите более серьёзные потенциальные проблемы в виде блокировок, например, то поделитесь…
Я и не против, отвечаю же вам. )
Ну, не все же так хорошо понимают работу тасков, как вы, например. Как-никак увидев 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 где это, в привычном понимании, равноценная замена, не получить другое поведение программы.
А что в документации? Если её нет, а просто интерфейс с таском?
Как вы относитесь к потенциальной фиче (частному случаю Check паттерна, родственного With)?

/* true|false */
GetModel() is {} m // null check

/* true|false */
GetModel() is {Name is {} s, City is var c} m

Тогда вопрос, как мне быть уверенным, что таск у меня вообще начнёт выполняться, а не просто вернётся в вызывающий метод?
Никак вам не различить эти ситуации. То, когда начнется выполнение, зависит от того, что внутри AnyTask, а не снаружи.
Если правильно понимаю вас, то при наличии интерфейса, как у меня, и абстрагируясь от конкретной имплементации документа, мне нужно явно указывать await у Load, чтобы гарантированно выполнить таск, верно? Или чего-то ещё не понимаю?
То есть ReadToEndAsync тоже сам начинает выполнять таск, как у меня при StartNew, не дожидаясь явного await?

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность