Я бы посмотрел на это: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html, это довольно интересно и должно обеспечить хорошее место для начала. Но, конечно, мы используем Java, поэтому мы можем работать лучше (или, может быть, хуже, потому что нет макросов :))
Насколько я понимаю, с сопрограммами у вас обычно есть производитель и потребитель сопрограмма (или, по крайней мере, это самая распространенная модель). Но семантически вы не хотите, чтобы производитель звонил потребителю или наоборот, потому что это вводит асимметрию. Но учитывая то, как работают языки, основанные на стеке, нам нужно, чтобы кто-то делал вызов.
Итак, вот очень простая иерархия типов:
public interface CoroutineProducer<T>
{
public T Produce();
public boolean isDone();
}
public interface CoroutineConsumer<T>
{
public void Consume(T t);
}
public class CoroutineManager
{
public static Execute<T>(CoroutineProducer<T> prod, CoroutineConsumer<T> con)
{
while(!prod.IsDone()) // really simple
{
T d = prod.Produce();
con.Consume(d);
}
}
}
Теперь, конечно, самая сложная часть - это реализация интерфейсов, в частности, сложно разбить вычисление на отдельные этапы. Для этого вам, вероятно, понадобится целый набор постоянных управляющих структур . Основная идея заключается в том, что мы хотим симулировать нелокальную передачу управления (в конце концов, это похоже на то, что мы имитируем goto
). Мы в основном хотим отойти от использования стека и pc
(счетчик программ), сохраняя состояние наших текущих операций в куче, а не в стеке. Поэтому нам понадобится куча вспомогательных классов.
Например:
Допустим, в идеальном мире вы хотели написать потребителя, который выглядел бы так (psuedocode):
boolean is_done;
int other_state;
while(!is_done)
{
//read input
//parse input
//yield input to coroutine
//update is_done and other_state;
}
нам нужно абстрагировать локальную переменную, такую как is_done
и other_state
, и нам нужно абстрагировать сам цикл while, потому что наша yield
like-операция не будет использовать стек. Итак, давайте создадим абстракцию цикла while и связанные классы:
enum WhileState {BREAK, CONTINUE, YIELD}
abstract class WhileLoop<T>
{
private boolean is_done;
public boolean isDone() { return is_done;}
private T rval;
public T getReturnValue() {return rval;}
protected void setReturnValue(T val)
{
rval = val;
}
public T loop()
{
while(true)
{
WhileState state = execute();
if(state == WhileState.YIELD)
return getReturnValue();
else if(state == WhileState.BREAK)
{
is_done = true;
return null;
}
}
}
protected abstract WhileState execute();
}
Основной трюк здесь состоит в том, чтобы переместить локальные переменные в class переменные и превратить блоки области видимости в классы, что дает нам возможность повторно войти в наш "цикл" после возвращая наше возвращаемое значение.
Теперь реализуем нашего производителя
public class SampleProducer : CoroutineProducer<Object>
{
private WhileLoop<Object> loop;//our control structures become state!!
public SampleProducer()
{
loop = new WhileLoop()
{
private int other_state;//our local variables become state of the control structure
protected WhileState execute()
{
//this implements a single iteration of the loop
if(is_done) return WhileState.BREAK;
//read input
//parse input
Object calcluated_value = ...;
//update is_done, figure out if we want to continue
setReturnValue(calculated_value);
return WhileState.YIELD;
}
};
}
public Object Produce()
{
Object val = loop.loop();
return val;
}
public boolean isDone()
{
//we are done when the loop has exited
return loop.isDone();
}
}
Подобные приемы могут быть выполнены для других базовых структур потока управления. В идеале вы должны создать библиотеку этих вспомогательных классов, а затем использовать их для реализации этих простых интерфейсов, которые в конечном итоге дадут вам семантику сопрограмм. Я уверен, что все, что я здесь написал, может быть обобщено и значительно расширено.