Все знают, что синхронное выполнение длительных операций – это плохо. Это плохо по многим причинам, начиная с того, что синхронные решения приводят к подвисанию пользовательского интерфейса, плохой масштабируемости и расширяемости, заканчивая тем, что вы не сможете воспользоваться всеми возможностями многоядерного процессора даже вашего домашнего компьютера, не говоря уже за преимущества асинхронного ввода/вывода, эффективность которого проявляется даже на одноядерных машинах. О теме асинхронности и многопоточности говорят на каждом шагу, начиная от небезызвестных товарищей вроде Джеффри Рихтера, Джо Даффи или Джозефа Албахари, заканчивая множеством статей на каждом втором техническом сайте и обязательным вопросом на собеседовании типа «А сколько вы знаете способов выполнить операцию асинхронно?».
Авторы книг и статей, любят эту тему, потому что она обширная и сложная, да в последнее время еще и модная. Интервьюеры же любят ее за то, что вариантов ответа на этот вопрос в контексте платформы .Net много, и у каждого интервьюера может быть свой любимый способ доказать соискателю, что тот чего-то все же не знает. С точки зрения простого программиста использование всех современных инструментов для работы с многопоточностью, несмотря на все обещания их разработчиков, все еще остается делом непростым и легко может привести к вывиху мозга, а малейшая неосторожность – к отстрелу нескольких ценных конечностей. Одной из причин, почему так происходит, является то, что вся поддержка многопоточности в языке C# заканчивается оператором lock, а все остальное прикручено к нему благодаря сторонним (или не очень) библиотекам, начиная с BCL и RX, заканчивая PowerThreading и другими велосипедами, различного вида и формы (хотя, конечно же, главная причина заключается в том, что тема, эта сама по себе, не просто сложная, а очень сложная, и все попытки ее упростить настолько, чтобы она была понятна домохозяйкам, успехом не увенчалась, и едва ли увенчается когда-либо).
Я же никакие свои велосипеды изобретать не собираюсь, а сосредоточусь лишь на различных способах работы с асинхронной моделью программирования (AsynchronousProgrammingModel, APM) на платформе .Net на примере идеи, предложенной Джеффри Рихтером с его классом AsyncEnumerator.