Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: 1 2 [3] 4   Вниз
  Печать  
Автор Тема: Сопрограммы в языке программирования C  (Прочитано 158171 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #60 : 26-10-2010 12:33 » 

npak, как вариант, почему бы тогда не попробовать написать микроядро на Си? по крайней мере платформо-независимую часть
Да, я, наверное, слишком общё выразился. На асме надо написать микро-микро ядро Улыбаюсь
Что в него должно войти - обработка прерываний, работа с блоками памяти (memcpy/memmove/memset/memcmp), создание и переключение контекстов
Все остальное - планировщик, динамическая память, порты ввода-выввода - можно написать машинно-независимо на С/С++
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #61 : 26-10-2010 15:18 » 

2. Рекомендую изменить название на менее общее

Если большинство будет "за", то я не против, конечно... Хотя в заголовке обещали сопрограммы на С, начинаем читать - а там и правда сопрограммы на C. Вроде и не обманули.
м анекдоте про песни "Битлз" в перепевке Рабиновича, а не хотелось бы.

IMHO, это решать тебе, как автору перевода и, к тому же, задумки целой серии статей.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #62 : 26-10-2010 16:39 » 

IMHO, это решать тебе, как автору перевода и, к тому же, задумки целой серии статей.

Я бы оставил, как есть. Во-первых, из уважения к автору. Во-вторых, вроде бы название не вводит в заблуждение, что автор обещал, о том и рассказал. В-третьих, на оригинал есть ссылки, хоть и не слишком много, и людям может прийти в голову поискать перевод.

Ну а если концепция сопрограмм не всем сразу понятна - так это дело поправимое, в литературе на этот счет пробелы. Немного поработаем, и желающие смогут почитать и разобраться. 3-4 статьи, думаю, будет достаточно.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #63 : 26-10-2010 20:31 » 

Если мой аргумент послужит  авторитетом - оставляем как есть.
!. С точки зрения Seo поиск будет вестись по сопрограммам.
2. Чайнику - непонятно и хрен с ним, это ему не поможет.
3. Профи? Хм... только в том случае если он не знаком с таким понятием...

зы...
Могу олубликовать перевод на хабре - посмотрим  реакцию. А че? та хрен с ней с репой. В личном блоге сколько хочу Улыбаюсь
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #64 : 26-10-2010 22:16 » 

!. С точки зрения Seo поиск будет вестись по сопрограммам.

Логично. Лично у меня глаз зацепился за ключевое сочетание "сопрограмма" и "язык C". То, что оно внутри сделано методом Даффа, - дело десятое. Мне ведь главное - ехать, а не "шашечки"...

зы...
Могу олубликовать перевод на хабре - посмотрим  реакцию. А че? та хрен с ней с репой. В личном блоге сколько хочу Улыбаюсь

Мне кажется, риск минимален. Материал-то качественный. Если репа треснет пострадает, вывод однозначен: ресурс не созрел, пусть резвятся вокруг "Hello world". Ну а если оценят, выиграют в итоге все.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #65 : 07-11-2010 09:35 » 

Пока читал, меня преследовало чувство дежавю, поэтому возникли дополнения, выходящие за пределы языка C.

Дополнение 1. Урезанным вариантом сопрограммы, тем не менее показывающим "родовую" связь сопрограмм и подпрограмм (subroutines), можно считать подпрограммы языка Basic. Подпрограмма отличается от процедуры и функции тем, что не имеет передаваемых через стек параметров и хранящихся в стеке локальных переменных - все переменные общие с основной программой, однако хранит в стеке адрес возврата - тем отличается от обычного GoTo. Где это можно применять. Например, как в статье, требуется написать некий сложный управляющий алгоритм, в нужные моменты времени передающий управление блоку операторов для выполнения какого-то действия.
Код: (Text)
Sub Control(FlagA, FlagB As Boolean, X As Integer, Y As Double)

  ' Управляющий алгоритм, в разных местах вызывающий ОДНО И ТО ЖЕ действие.
  If Not FlagA And FlagB Then
    If X > 10 And X < 20 Then
      GoSub Action
    End If
  Else
      GoSub Action
  End If
       
  ' Обязательно выход из процедуры, чтобы избежать несанкционированного
  ' выполнения действия.
  Exit Sub
       
Action:
  ' Действие: любая последовательность операторов, недостаточно сложная или
  ' сильно привязанная к текущим значениям локальных переменных, чтобы не
  ' хотелось выносить её в отдельную процедуру/функцию.
  Print Y

  Return
End Sub
Если не делать GoSub, то либо придётся делать copy-paste куска кода под меткой Action, либо выносить этот кусок кода в отдельную процедуру. Первое ухудшает сопровождаемость кода и перегружает деталями управляющий алгоритм, второе не всегда выгодно, если для выполнения действия нужна большая часть локальных переменных.

Это, конечно, не является парой взаимодействующийх сопрограмм, здесь лишь управляющий алгоритм прерывается на время для выполнения подпрограммы. Тем не менее оператор Return в подпрограмме возвращает машину к управляющему алгоритму в точке его прерывания, чтобы продолжить выполнение этого алгоритма. Т.е. основное преимущество сопрограмм - ясность алгоритма - сохраняется.

Дополнение 2. Нечто подобное подпрограммам языка Basic мне приходилось реализовывать в языке SQL - и там как раз использовался метод, очень похожий на машину Даффа.
Код: (Text)
CREATE PROCEDURE Control(@FlagA AS bit, @FlagB AS bit, @X AS int, @Y AS float)
AS
        DECLARE @State AS int
        SET @State = 0

Control:
IF @State = 0
        GOTO State0
ELSE IF @State = 1
        GOTO State1
ELSE IF @State = 2
        GOTO State2
ELSE
        RETURN

State0:
        -- Управляющий алгоритм, в разных местах вызывающий ОДНО И ТО ЖЕ действие.
        IF @FlagA = 0 AND @FlagB = 1
                BEGIN
                        IF @X > 10 AND @X < 20
                                BEGIN
SET @State = 1
                                        GOTO Action
State1:
                                END
                END
        ELSE
                BEGIN
SET @State = 2
                        GOTO Action
State2:
                END

        -- Обязательно выход из процедуры, чтобы избежать несанкционированного
        -- выполнения действия.
        RETURN

Action:
        -- Действие: любая последовательность операторов, недостаточно сложная или
        -- сильно привязанная к текущим значениям локальных переменных, чтобы не
        -- хотелось выносить её в отдельную процедуру
        PRINT @Y

        GOTO Control
GO
Конечно в плане читатебельности кода выглядит это довольно неаккуратно, но делает то же самое.

Дополнение 3. В современных языках то же самое может быть реализовано посредством:
  • Использования замыканий:
Код: (Ruby)
def control(flag_a, flag_b, x, y)
  action = lambda do
    puts y
  end

  if not flag_a and flag_b then
    if x > 10 and x < 20 then
      action.call
    end
  else
    action.call
  end
end
  • yield-конструкций и, опять же, использования замыканий:
Код: (Ruby)
def control(flag_a, flag_b, x)
  if not flag_a and flag_b then
    if x > 10 and x < 20 then
      yield
    end
  else
    yield
  end
end

flag_a, flag_b, x, y = false, true, 15, 3.14
control(flag_a, flag_b, x) do
  puts y
end
    Данный пример не совсем точен, поскольку локальные переменные функции control (если бы они там были), пришлось бы передавать через yield-возврат в основную программу, хотя в основной программе доступны все параметры функции control. Следует заметить, что yield return (в том же C#.NET) хотя и выглядит как "временный" выход из процедуры, на самом деле является неявным вызовом анонимного блока кода (или lambda-функции), работающего в замыкании внешней функции - т.е. полностью эквивалентен предыдущему примеру. В языке Ruby это видно более отчётливо.

Однако, повторюсь, что все эти случаи - это не сопрограммы, а подпрограммы.
« Последнее редактирование: 07-11-2010 09:40 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #66 : 08-11-2010 20:18 » 

Dale, спасибо за статью.

Сегодня применил этот трюк для кодирования обходчика конечного автомата. Работает отлично.

Выше в теме я агитировал за переключение контекстов, но для обходчиков автоматов трюк Даффа подходит больше, чем "легкие потоки" (fibers).  Дело в том, что каждый легкий поток "весит" порядка мегабайта, так как в контексте хранится отдельный сегмент стека. Когда в автомате тысячи состояний для хранения контекстов потоков нужны гигабайты памяти, а в случае использования сопроцедур объем хранимых данных на порядки меньше. В моем случае это 8 байт - 4 байта на номер строки и 4 байта для итерационной переменной. Гораздо меньше, чем мегабайт, не так ли Ага

Замечание для тех, кто захочет попробовать сопроцедуры в MS Visual Studio: необходимо указывать компилятору опцию Zi (Program Database). Если будет выбрано ZI (Program Database for Edit and Continue), то компиляция вызовет ошибку error C2051: case expression not constant
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #67 : 08-11-2010 20:42 » 

Dale, спасибо за статью.

Сегодня применил этот трюк для кодирования обходчика конечного автомата. Работает отлично.

Пожалуйста. Рад, что пригодилось Улыбаюсь

Как только увидел оригинал статьи, я сразу понял, что просто должен ей поделиться. Интернет переполнен "чайниковыми" учебниками, но после прочтения Кернигана и Ричи дальше читать почти нечего. Подобные материалы нужно вымывать из шлака, как крупицы золота.

Выше в теме я агитировал за переключение контекстов, но для обходчиков автоматов трюк Даффа подходит больше, чем "легкие потоки" (fibers).  Дело в том, что каждый легкий поток "весит" порядка мегабайта, так как в контексте хранится отдельный сегмент стека.

То, что накладно на PC, на "легких" микроконтроллерах в принципе невозможно, поскольку их аппаратные ресурсы на несколько порядков меньше. Это основная причина, по которой я так уцепился за статью и попытался убедить, что методика вовсе не так нелепа, как может показаться при беглом прочтении.

Замечание для тех, кто захочет попробовать сопроцедуры в MS Visual Studio: необходимо указывать компилятору опцию Zi (Program Database). Если будет выбрано ZI (Program Database for Edit and Continue), то компиляция вызовет ошибку error C2051: case expression not constant

На этот счет в статье был намек:

Отметим, что с Visual C++ версии 6 трюк с сопрограммами не проходит, поскольку его отладочное состояние по умолчанию (Program Database for Edit and Continue) делает нечто странное с макросом __LINE__. Чтобы скомпилировать программу с использованием сопрограмм в среде VC++ 6, вы должны выключить опцию «Edit and Continue». (В свойствах проекта перейдите на закладку «C/C++», категория «General», установка «Debug info». Выберите любую опцию, кроме «Program Database for Edit and Continue».)

Наверное, нужно как-то поярче выделить этот абзац, чтобы он сильнее бросался в глаза. У тех, кто будет пытаться повторить примеры, не дочитав статью до конца, могут возникнуть аналогичные трудности.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #68 : 08-11-2010 21:39 » 

Dale, посмотри начало топика - сделал выделение. Так подойдет?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #69 : 08-11-2010 23:49 » 

Да, очень хорошо визуально выделяется на общем фоне. Подходящий стиль для оформления примечаний.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #70 : 10-11-2010 10:05 » 

Показал первый раз коллегам. Присваивание 
Код:
state=__LINE__; return x; case __LINE__:;
привело народ в экстаз.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #71 : 10-11-2010 13:49 » 

npak, в позитивный экстаз или наоборот?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #72 : 10-11-2010 16:26 » 

в когнитивный диссонанс (модно нонче Отлично )

кстати, не совсем понятно расположение case. Ведь, сли __LINE__ - это номер строки, то вернёмся потом опять к оператору state=...;
А не должно ли быть так:
Код:
state=__LINE__+1; return x;
case __LINE__+1:
?
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #73 : 10-11-2010 16:31 » 

Леш, не к началу строки, а к case!
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #74 : 10-11-2010 16:36 » 

case - это же метка. Я провто не знаю, как там по стандарту расположение метки обрабатывается
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #75 : 10-11-2010 17:14 » 

Не метки это. Или, во всяком случае, они позиционируются не на строках, а на операторах.

Код:
switch (cond) { case 0: x = 1; break; case 1: x = 10; break; default: x = 0; }

Валидно.
« Последнее редактирование: 10-11-2010 17:16 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #76 : 10-11-2010 17:40 » 

тогда понятно. Используется тот факт, что номер строки не повторяется (только в одну строку два таких фокуса нельзя записать)
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #77 : 10-11-2010 17:48 » 

Если бы был в препроцессоре какой-то генератор уникальных последовательностей, то можно было и несколько в строку. Только ведь так все равно не пишут - значит оно и не нужно.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #78 : 10-11-2010 22:51 » 

RXL, вообще.... я подозревал что генератор такой есть но я не смог его найти)

Добавлено через 15 минут и 31 секунду:
кстати... а нельзя дефайном задать что нить  типа:
#ifndef qqq
  #define qqq 1
#else
   #define qqq    ((qqq)+1)  
#endif
чтобы каждый раз переопределялось?


Добавлено через 8 минут и 12 секунд:
кстати....хотя это и бессмысленно в данном случае...
"Директива #line дает команду компилятору сменить хранимые им номер строки и имя файла на данные. "
« Последнее редактирование: 10-11-2010 23:06 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #79 : 11-11-2010 05:20 » 

Ochkarik, нет, дефайн не бывает рекурсивным Улыбаюсь
Записан

npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #80 : 11-11-2010 06:57 » 

Рома, экстаз был позитивный.  Я вообще затрудняюсь представить негативный экстаз Улыбаюсь


кстати, не совсем понятно расположение case. Ведь, сли __LINE__ - это номер строки, то вернёмся потом опять к оператору state=...;
А не должно ли быть так:
Код:
state=__LINE__+1; return x;
case __LINE__+1:
Алексей, при подстановке препроцессор вытянет несколько строк макроса в одну. Поэтому должно быть line = __LINE__ а не __LINE__+1 , так как case будет на одной строке с присваиванием.

Разумеется, из этого следует, что несколько возвратов нельзя размещать на одной строке, так как сгенерируются две одинаковые метки.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #81 : 11-11-2010 07:05 » 

npak, в моём посте был не макрос Улыбаюсь
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #82 : 11-11-2010 07:06 » 

хотя да, не-макрос и не подставишь несколько раз Улыбаюсь
Записан

npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #83 : 11-11-2010 08:08 » 

Попробуйте скомпилировать под MS VC с включенной опцией Debug Database for Edit and Continue - получите незабываемые переживания Улыбаюсь
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #84 : 11-11-2010 08:20 » 

Попробуйте скомпилировать под MS VC с включенной опцией Debug Database for Edit and Continue - получите незабываемые переживания Улыбаюсь
хм, у меня в обоих вариантах ведёт себя одинаково -
 error C2051: значение выражения для варианта выбора не является константой
(__LINE__ не определён )  Улыбаюсь
Записан

npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #85 : 11-11-2010 09:30 » new

в обоих - это в каких? Если отключить "Edit and Continue", то у меня собирается
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #86 : 11-11-2010 09:30 » 

В догонку - у меня стоит десятая Visual Studio
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #87 : 11-11-2010 09:45 » 

Судя по тому, что рекомендацию по отключению "Edit and Continue" сам Тэтхем дает для 6-й версии Visual Studio, имеет смысл предположить, что и в промежуточных версиях это должно работать.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #88 : 11-11-2010 09:45 » 

в обоих - это с галкой и без. Студия 2008
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #89 : 12-11-2010 19:08 » 

Ради интереса собрал в C# решение на основе yield return. Его неудобства:

  • сопрограмма обязана возвращать тип IEnumerable, чтобы иметь право использовать внутри себя yield return;
  • сопрограмме запрещено использовать внутри себя try-catch (особенности работы yield return);
  • yield return формально что-то должен возвращать - хотя бы null;
  • запрещено использование анонимных методов (особенности работы yield return).

Напишем вспомогательный класс, который будет принимать в себя перечень сопрограмм и в этом порядке работать с ними. Сопрограммы будем ему передавать посредством делегатов. Я класс сделал чуть сложнее - он работает асинхронно, что позволяет извне прервать работу взаимно зациклившихся сопрограмм, а также узнавать их статус (т.е. реализовать некий внешний монитор вычислительного процесса) но в общем-то это не обязательно.
Код: (Text)
    public delegate IEnumerable CoProgram();

    public enum CoProgramState
    {
        Unknown,
        Waiting,
        Running,
        Finished
    }

    public class CoProgramContext: IDisposable
    {
        private enum State
        {
            New,
            Actual,
            Old
        }

        private class CoProgramInfo
        {
            public CoProgram CoProgram;
            public IEnumerator Run;
            public State State;
        }

        private List<CoProgramInfo> coprograms;
        private Thread thread;
        private CoProgramInfo currentCoprogram;

        public CoProgramContext(params CoProgram[] coprograms)
        {
            this.coprograms = new List<CoProgramInfo>();
            this.Add(coprograms);
            this.Run();
        }

        public CoProgramState Check(CoProgram coprogram)
        {
            lock (this.coprograms)
            {
                foreach (CoProgramInfo info in this.coprograms)
                {
                    if (info.CoProgram == coprogram)
                    {
                        if (info.State == State.Old)
                        {
                            return CoProgramState.Finished;
                        }
                        else
                        {
                            if (info.Equals(this.currentCoprogram))
                            {
                                return CoProgramState.Running;
                            }
                            else
                            {
                                return CoProgramState.Waiting;
                            }
                        }
                    }
                }
                return CoProgramState.Unknown;
            }
        }

        public void Dispose()
        {
            this.Stop();
        }

        private void Add(params CoProgram[] coprograms)
        {
            foreach (CoProgram coprogram in coprograms)
            {
                CoProgramInfo info = new CoProgramInfo();
                info.CoProgram = coprogram;
                info.Run = null;
                info.State = State.New;
                lock (this.coprograms)
                {
                    this.coprograms.Add(info);
                }
            }
        }

        private void Run()
        {
            this.thread = new Thread(new ThreadStart(this.Loop));
            this.thread.Start();
        }

        private void Stop()
        {
            this.thread.Abort();
            this.thread = null;
        }

        private void Loop()
        {
            try
            {
                while (true)
                {
                    int i = 0;
                    while (true)
                    {
                        lock (this.coprograms)
                        {
                            if (i >= this.coprograms.Count)
                            {
                                break;
                            }
                            this.currentCoprogram = this.coprograms[i];
                        }
                        switch (this.currentCoprogram.State)
                        {
                            case State.New:
                                this.currentCoprogram.State = State.Actual;
                                this.currentCoprogram.Run = this.currentCoprogram.CoProgram().GetEnumerator();
                                break;
                            case State.Actual:
                                if (!this.currentCoprogram.Run.MoveNext())
                                {
                                    this.currentCoprogram.State = State.Old;
                                    this.currentCoprogram.Run = null;
                                }
                                break;
                        }
                        i += 1;
                    }
                }
            }
            catch
            {
            }
        }
    }

Далее на базе этого класса собрал решение из двух "общающихся" алгоритмов, реализующих игру отгадывания числа.
Код: (Text)
    static class MyCoPrograms
    {
        public static CoProgramContext Run()
        {
            return new CoProgramContext(
                    new CoProgram(MyCoPrograms.Produce),
                    new CoProgram(MyCoPrograms.Consume)
                );
        }

        // Глобальные переменные, управляющие процессом.
        private enum Answer
        {
            Undefined,
            Less,
            Great,
            Equal
        }
        private static int checkNumber = int.MinValue;
        private static Answer answer = Answer.Undefined;

        private static IEnumerable Produce()
        {
            Console.WriteLine("Produce: Начало.");
            Console.WriteLine("Produce: Загадываем число от 1 до 100.");
            Random random = new Random();
            int number = random.Next(100) + 1;
            Console.WriteLine("Produce: Загадано число {0}.", number);
            do
            {
                Console.WriteLine("Produce: Передаём управление Consume для отгадывания числа.");
                yield return null;
                Console.WriteLine("Produce: Проверяем ответ Consume.");
                if (checkNumber == number)
                {
                    Console.WriteLine("Produce: Consume отгадал число.");
                    answer = Answer.Equal;
                }
                else
                {
                    if (checkNumber < number)
                    {
                        Console.WriteLine("Produce: Загаданное число больше предложенного.");
                        answer = Answer.Great;
                    }
                    else
                    {
                        Console.WriteLine("Produce: Загаданное число меньше предложенного.");
                        answer = Answer.Less;
                    }
                }
            }
            while(answer != Answer.Equal);
            Console.WriteLine("Produce: Конец.");
        }

        private static IEnumerable Consume()
        {
            Console.WriteLine("Consume: Начало.");
            int min = 1, max = 100;
            while (answer != Answer.Equal)
            {
                Console.WriteLine("Сonsume: Выбираем число между {0} и {1}.", min, max);
                checkNumber = (min + max) / 2;
                Console.WriteLine("Consume: Предлагаем число {0}.", checkNumber);
                Console.WriteLine("Consume: Передаём управление Produce для проверки числа.");
                yield return null;
                switch (answer)
                {
                    case Answer.Great:
                        Console.WriteLine("Consume: Загаданное число больше предложенного.");
                        min = checkNumber;
                        break;
                    case Answer.Less:
                        Console.WriteLine("Consume: Загаданное число меньше предложенного.");
                        max = checkNumber;
                        break;
                }
            }
            Console.WriteLine("Consume: Конец.");
        }
    }
Сопрограммы обмениваются информацией через глобальные переменные. Эти глобальные переменные и сопрограммы собраны в единый класс, инкапсулирующий всё это хозяйство.

Основная программа ждёт, пока пользователь не нажмёт какую-нибудь клавишу, после чего завершает работу программы (в том числе и принудительно сопрограмм, если они ещё не завершились сами).
Код: (Text)
    class Program
    {
        static void Main(string[] args)
        {
            using(CoProgramContext context = MyCoPrograms.Run())
            {
                Console.ReadKey();
            }
        }
    }

Результат:
Produce: Начало.
Produce: Загадываем число от 1 до 100.
Produce: Загадано число 14.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Начало.
Сonsume: Выбираем число между 1 и 100.
Consume: Предлагаем число 50.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Загаданное число меньше предложенного.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Загаданное число меньше предложенного.
Сonsume: Выбираем число между 1 и 50.
Consume: Предлагаем число 25.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Загаданное число меньше предложенного.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Загаданное число меньше предложенного.
Сonsume: Выбираем число между 1 и 25.
Consume: Предлагаем число 13.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Загаданное число больше предложенного.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Загаданное число больше предложенного.
Сonsume: Выбираем число между 13 и 25.
Consume: Предлагаем число 19.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Загаданное число меньше предложенного.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Загаданное число меньше предложенного.
Сonsume: Выбираем число между 13 и 19.
Consume: Предлагаем число 16.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Загаданное число меньше предложенного.
Produce: Передаём управление Consume для отгадывания числа.
Consume: Загаданное число меньше предложенного.
Сonsume: Выбираем число между 13 и 16.
Consume: Предлагаем число 14.
Consume: Передаём управление Produce для проверки числа.
Produce: Проверяем ответ Consume.
Produce: Consume отгадал число.
Produce: Конец.
Consume: Конец.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: 1 2 [3] 4   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines