Ответ 1
Coroutines - чрезвычайно мощный метод, используемый для эмуляции типов функций, поддерживаемых async/await в .net4.5, но в более ранних версиях (С# >= v2.0).
Microsoft CCR (прочитайте) также использует (использует?) этот подход.
Позвольте получить одну вещь. yield
один недействителен и всегда следует либо return
, либо break
.
Подумайте о стандартном IEnumerator (который не дает сообщений управления потоком).
IEnumerator YieldMeSomeStuff()
{
yield "hello";
Console.WriteLine("foo!");
yield "world";
}
Сейчас:
IEnumerator e = YieldMeSomeStuff();
while(e.MoveNext())
{
Console.WriteLine(e.Current);
}
Какой результат?
hello foo! world
Обратите внимание, что во второй раз, когда мы вызывали MoveNext
, до того, как Enumerator дал "мир", в Enumerator пробежал некоторый код. Это означает, что в Enumerator мы можем написать код, который будет выполняться до тех пор, пока он не достигнет инструкции yield return
, а затем просто остановится, пока кто-то не назовет MoveNext
(удобно, когда все состояния/переменные аккуратно захвачены, поэтому мы можем подобрать, где мы прекращено). После вызова MoveNext
следующий бит кода после оператора yield return
может работать до тех пор, пока не будет достигнут другой yield return
. Таким образом, теперь мы можем контролировать выполнение кода между операторами yield return
с вызовом MoveNext
в Enumerator.
Теперь, скажем, вместо того, чтобы уступать строки, наш Enumerator должен был выдать сообщение, которое говорит вызывающему абоненту MoveNext
, "пожалуйста, обернитесь для x (waitTime) секунд, прежде чем вы снова вызовете MoveNext
". Вызывающий звонит, чтобы "понять" множество сообщений. Эти сообщения всегда будут соответствовать следующим образом: "Подождите, пока что-то произойдет, прежде чем вызывать MoveNext
снова".
Теперь у нас есть мощное средство приостановки и перезапуска кода, требующего выполнения других условий, прежде чем он сможет продолжить, без необходимости писать эту функциональность в другой метод, например, делать асинхронные вещи без сопрограмм. Без сопроцессоров вы должны пройти вокруг ужасного объекта состояния aync, который вам нужно будет вручную собрать, чтобы зафиксировать состояние между концом одного метода и началом другого после некоторого асинхронного материала). Coroutines устраняет это, потому что область сохраняется (с помощью магии компилятора), поэтому ваши локальные переменные сохраняются в течение длительного времени, асинхронные вещи.
StartCoroutine
просто запускает весь процесс. Он вызывает MoveNext
в Enumerator... в Enumerator запускается некоторый код... Перечислитель выводит управляющее сообщение, которое сообщает код в StartCoroutine
, когда вызывать MoveNext
снова. Этого не должно быть в новом потоке, но оно может быть полезно в многопоточных сценариях, потому что мы можем вызывать MoveNext
из разных потоков и контролировать, где выполняется работа.