Сегодня, совершенно неожиданно для себя, открыл Coroutines.
Пример их использования, который я нашел:
import multitask
import time
def c1():
for i in range(3):
print 'c1'
yield i
def c2():
for i in range(3):
print 'c2'
yield i
multitask.add(c1())
multitask.add(c2())
multitask.run()
Но данный пример показывает использование coroutines в многопоточной
программе, хотя в интернете я находил утверждения, что они могут быть
полезны и в однопоточных программах.
Собственно, два вопроса:
1. Буду благодарен за пример проблемы, в решении которой coroutines не
притянуты за уши.
using System;
using System.IO;
using System.Collections;
public class OddWordProblem {
[STAThread]
static void Main(string[] args) {
StreamWriter output = new StreamWriter(args[1]);
new OddWordProblem().Solve(new StreamReader(args[0]), output);
output.Flush();
}
ArrayList currentWord = new ArrayList();
char aSeparator;
bool isForward=true;
bool isWordEnded=false;
void Solve(StreamReader theInput, StreamWriter theOutput) {
int nextChar;
while ((nextChar = theInput.Read())!=-1) {
char aChar = (char)nextChar;
if(aChar==' '||aChar=='.')EndOfWord(theOutput, aChar);
else MiddleOfWord(theOutput, aChar);
}
theOutput.Write(aSeparator);
}
void MiddleOfWord(StreamWriter theOutput, char aChar) {
if(isWordEnded) {
isWordEnded = false;
isForward = !isForward;
theOutput.Write(aSeparator);
}
currentWord.Add(aChar);
}
void EndOfWord(StreamWriter output, char nextChar) {
if(!isForward)currentWord.Reverse();
foreach(char aChar in currentWord)
output.Write(aChar);
aSeparator = nextChar;
currentWord = new ArrayList();
isWordEnded = true;
}
}
Мы всего лишь засунули символы в буфер и вызвали метод .Reverse(). Или я
чего то не понимаю?
2. Вопрос по реализации. Если я правильно понимаю, между вызовами coroutine
ее состояние(локальные переменные и точка возврата) должна где-то
сохраняться. Где она сохраняется? Неужели в стеке?
DAS>В решении на C# coroutines я вообще не увидел
Ну так это первый C#... там корутин нету. Во втором есть yield return.
DAS>2. Вопрос по реализации. Если я правильно понимаю, между вызовами coroutine ее состояние(локальные переменные и точка возврата) должна где-то сохраняться. Где она сохраняется? Неужели в стеке?
Зависит от реализации.
В случае C# и Nemerle генерируется некий класс который и хранит состояние.
В других реализациях может быть иначе вплоть до инлайна корутины.
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, DemAS, Вы писали:
DAS>Собственно, два вопроса:
DAS>1. Буду благодарен за пример проблемы, в решении которой coroutines не DAS>притянуты за уши.
Тоже не так давно открыл для себя их, но в однопоточной среде и С++
Вот пара примеров: Генератор простых чисел
Боюсь решение данной проблемы как раз притянуто за уши к генераторам.
DAS>2. Вопрос по реализации. Если я правильно понимаю, между вызовами coroutine DAS>ее состояние(локальные переменные и точка возврата) должна где-то DAS>сохраняться. Где она сохраняется? Неужели в стеке?
А почему бы и нет? Вот в реализации на С++, где создается объект класса генератора, там и хранится состояние.
Coroutines are functions or procedures that save control state between calls (as opposed to, but very similar to, Generators, such as Random Number Generators, that save data state between calls).
Может я не прав, но мне кажется, что у тебя генератор.
У меня обычный случай применения: либо итератор для разлапистой структуры данных, либо низкоуровневый кирпич для чего-нибудь более интересного. На основе корутин (по крайней мере если они кооперативные треды как в луа) можно реализовать более высокоуровневый набор примитивов, например CSP — OCCAM — Limbo — Stackless Python. Events vs. Threads Bell Labs and CSP Threads
DAS>2. Вопрос по реализации. Если я правильно понимаю, между вызовами coroutine DAS>ее состояние(локальные переменные и точка возврата) должна где-то DAS>сохраняться. Где она сохраняется? Неужели в стеке?
В луа корутина — просто кооперативный тред со своим стеком (примерно как в erlang). Занимает примерно 1K(тоже примерно как в erlang) — можно завести их сотни тысяч.
DAS>Coroutines are functions or procedures that save control state between calls (as opposed to, but very similar to, Generators, such as Random Number Generators, that save data state between calls).
DAS>Может я не прав, но мне кажется, что у тебя генератор.
Из статьи я понял, что корутины это некие программные компоненты, передающие друг другу право на исполнение, исполняющиеся перемежающимся способом. Генераторы же просто выдают некоторые значения, когда их вызывают, пока не завершатся.
А еще там написано:
Generators are also a generalisation of subroutines, but with at first sight less expressive power than coroutines; since generators are primarily used to simplify the writing of iterators, the yield statement in a generator does not specify a coroutine to jump to, but rather passes a value back to a parent routine. However, it is still possible to implement coroutines on top of a generator facility, with the aid of a top-level dispatcher routine that passes control explicitly to child generators identified by tokens passed back from the generators
Т.е. тот код, что ты привел в корне (на Эрланге?), как раз использует генераторы, а их совместная работа обеспечивается многозадачным диспатчером верхнего уровня.
"R.K." <42491@users.rsdn.ru> writes:
> Т.е. тот код, что ты привел в корне (на Эрланге?), как раз использует > генераторы, а их совместная работа обеспечивается многозадачным > диспатчером верхнего уровня.
На Python.
Глава из книги Expert Python Programming.
Там этот пример приводится в разделе про coroutines.
Согласен, что пример неудачный — поэтому и ищу более понятный.
Твой код посмотрю чуть позже — просто у меня с C++ не супер и для изучения
нужна более спокойная обстановка.
Здравствуйте, DemAS, Вы писали:
DAS>1. Буду благодарен за пример проблемы, в решении которой coroutines не DAS>притянуты за уши.
Сталкивался с тамим, не знаю на сколько за уши:
У нас допустим есть большой блок данных, который нужно обработать несколькими алгоритмами и выбрать лучший результат. Причём алгоритмы уже есть:
result alg1(ISequentialStream * data);
result alg2(ISequentialStream * data);
...
result algN(ISequentialStream * data);
Чтобы не читать этот большой блок данных N раз, а сделать всё в один проход (допустим, блок на DVD диске или возвращается по HTTP запросу), можно в реализации ISequentialStream читать небольшими кусками, если какой-то алгоритм требует продолжения-переключать на другой, когда все потребуют продолжения — переходить на новый кусок.
DAS>2. Вопрос по реализации. Если я правильно понимаю, между вызовами coroutine DAS>ее состояние(локальные переменные и точка возврата) должна где-то DAS>сохраняться. Где она сохраняется? Неужели в стеке?
В С/C++ для Windows можно применять фиберы (CreateFiber, etc). Их контекст такой же как контекст потока: свой стек, занимающий целый мегабайт ВАП, и переключение не очень дешевое.