Pull to refresh

Comments 12

Кое-как вылез из Википедии, при этом почувствовав себя быдлокодером.
Спасибо, интересная статья.
А это как-то декларируется, что PT_EXITED — это максимальная константа? Почему не проверяется на неравенство?
fib(&pt1, 1000, &value) < PT_EXITED

И да, отдельная благодарность за простое объяснение, что такое сопрограммы ("… вместо отношений программа-подпрограмма вполне уместны отношения сопрограмма-сопрограмма...").
Спасибо. Там несколько констант, означающих причину выхода, те что < PT_EXITED означают то, что поток еще не окончен, ждет чего-то или вернул промежуточное значение, > PT_EXITED значит, что поток прерван или дошел до конца
А, понял вопрос! Да, в данном конкретном случае можно написать fib(&pt1, 1000, &value) != PT_ENDED или fib(&pt1, 1000, &value) == PT_YIELDED, < PT_EXITED более общая проверка на завершение
Интересный подход.
Получается, если нужно запустить одну и ту же функцию в нескольких «потоках», то контекст каждой функции будет передаваться ей аргументом, правильно? И этот контекст будет заменять обычный набор стек+регистры+TLS в полноценных потоках.

Соответственно, если количество «потоков» может меняться во время работы программы, то всё это надо учитывать в собственном планировщике потоков, применяющем PT_SCHEDULE к каждому «потоку»?
Да, всё правильно, если нужно запустить один поток из другого потока, или нужно запустить несколько одинаковых потоков, контекст нужно хранить и передавать явно, например в структуре. Обычные функции можно вызывать обычным способом, их никто не прервёт до завершения. Если потоков переменное количество, удобно объединить контекст потока с его функцией в структуру и вести список таких структур. Тут простор для творчества большой :)
Примеры, приведенные в статье, писались под Windows, работают в MinGW и Visual Studio, но внимание! В Visual Studio, в конфигурации DEBUG, библиотека protothreads, в том виде как она есть, не компилируется!
Причина в том, что макрос __LINE__ в конфигурации DEBUG в VS почему-то из константы превращается в вызов функции, это легко лечится, если в файле lc-switch.h заменить
#define LC_SET(s) s = __LINE__; case __LINE__:

на
#define LC_SET(s) s = __COUNTER__+1; case (__COUNTER__):


#if defined(_MSC_VER) && defined(_DEBUG)
#define LC_SET(s) s = __COUNTER__+1; case (__COUNTER__):
#else
#define LC_SET(s) s = __LINE__; case __LINE__:
#endif

Enjoy ;)
Спасибо, статья хорошая и подход как продолжений так и замыканий хороший.
Задумался над следующим — в принципе, это все реализуемо через ООП, но, ИМХО «сопрограммный» подход более читабельный, и по идее, должен быть меньше оверхед связанный с ООП (VMT и проч.). Однако, для ООП есть паттерны, методики TDD…
Соответственно, интересно мнение сообщества — насколько велик предполагаемый оверхед и какие еще видятся достоинства/недостатки в паре ООП/сопрограммы.
Я думаю, что обернуть поток в объект — идея полностью оправдана, совершенно естественно связать поток с его контестом и всеми данными в одном объекте и при этом спрятать всё лишнее от планировщика, оверхед если и будет, то совсем незначительный.
Причина того, что это сделано на C — то, что такой подход более общий, еще существуют микроконтроллеры, для которых нет компилятора C++, или он очень дорог, или нужно поддерживать уже существующий проект на С
Про чистый С для микроконтроллеров понятно. Мне просто кажется, что в ряде случаев сопрограммный подход будет выигрывать и на «больших машинах». По крайней мере, обработка сложных структур данных — типа вашего примера с XML или реализации FSM логики, мне представляется более читабельным/сопровождабельным в сопрограммной формулировке, не знаю, так это или нет, надо попробовать…
Sign up to leave a comment.

Articles

Change theme settings