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

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

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

« Ответ #90 : 17-11-2010 15:07 » 

Опробовал это дело на двух группах студентов (на C# с yeild return) - хорошо пошло. Правда, судя по всему, они это воспринимают как некий хак, и нравится им именно "хитрость" такого решения.
Записан

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

us
Offline Offline

« Ответ #91 : 12-03-2011 16:16 » 

Код:
int function(void) {
    static int i, state = 0;
    switch (state) {
        case 0: goto LABEL0;
        case 1: goto LABEL1;
    }
    LABEL0: /* начало функции */
    for (i = 0; i < 10; i++) {
        state = 1; /* возвратиться к LABEL1 */
        return i;
        LABEL1:; /* продолжить выполнение сразу за точкой возврата */
    }
}

Тут проблема с одиннадцатым вызовом. Надо бы перед настоящим завершением функции обнулить state. Что бы функция опять пошла считать от 0 до 9-ти. Иначе потом будет тупо возврат неопределёнки.

Добавлено через 6 минут и 17 секунд:
В догонку:
Иначе LABEL0 не имеет смысл. state нигде не будет равно нулю, кроме как в начале.
« Последнее редактирование: 12-03-2011 16:22 от PredatorAlpha » Записан
RXL
Технический
Администратор

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

WWW
« Ответ #92 : 12-03-2011 20:49 » 

А зачем такое извращение? Задача запутаться?
Записан

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

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

WWW
« Ответ #93 : 18-07-2011 15:30 » 

Продолжаю применительно к этому: https://forum.shelek.ru/index.php/topic,26526.msg264626.html#msg264626

Вопросы:
1. А не выше ли шанс запутаться при написании программы?
2. По моему к обработчикам прерываний такой подход не применим. Я ошибаюсь? В статье (по ссылке) как раз обработчик событий показан.
3. С применением protothread потоки можно создавать динамически?
4. Я так понял, что в protothread два одинаковых (использующих один код) потока будут конфликтовать?
« Последнее редактирование: 18-07-2011 15:32 от RXL » Записан

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

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

WWW
« Ответ #94 : 18-07-2011 17:41 » new

Вопросы:
1. А не выше ли шанс запутаться при написании программы?

На мой взгляд - нет. Хотя наверняка это дело привычки и вкуса.

Обычно мы запутываемся либо в том, что нам плохо понятно, либо в том, что изобилует мелкими деталями, которые трудно удержать в голове. Вроде бы с протопотоками удастся благополучно избежать и того, и другого: они дают простые и понятные абстракции, которые прячут от нас детали реализации.

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

2. По моему к обработчикам прерываний такой подход не применим. Я ошибаюсь? В статье (по ссылке) как раз обработчик событий показан.

Да, для обработки прерываний кооперация явно противопоказана. Такие вещи лучше надежнее изолировать друг от друга.

3. С применением protothread потоки можно создавать динамически?

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

4. Я так понял, что в protothread два одинаковых (использующих один код) потока будут конфликтовать?

В такой простейшей реализации - однозначно да, ведь все переменные статические, а следовательно, функции нереентерабельны.

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

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

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

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

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

WWW
« Ответ #95 : 18-07-2011 17:50 » 

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

Ну, со остальным спасибо - стало понятнее. Пытаюсь прочесть статью целиком.

Добавлено через 29 минут и 49 секунд:
Я давно хочу заняться AVR, но пока все руки не доходят. Уже все нужное есть - вопрос собраться, спроектировать простой девайс и заняться освоением инструментов. По этому пока больше оперирую опытом 8051 и Z80 (хотя не думаю, что с AVR будут серьезные отличия).
Это как предисловие - описание моего потока суждения.

Смотрю в статье figure 1: radio_wake_eventhandler(). На мой взгляд, эта функция вызывается из цикла типа

Код: (ASM)
loop
    halt
    /*
        Проверка событий по регистрам устройств или переменным, установленным обработчиками прерываний.
        По итогам проверок вызываются различные функции типа radio_wake_eventhandler().
    */
    jmp loop

В AVR такой же метод работы?
« Последнее редактирование: 18-07-2011 18:20 от RXL » Записан

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

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

WWW
« Ответ #96 : 18-07-2011 19:59 » 

Я давно хочу заняться AVR, но пока все руки не доходят. Уже все нужное есть - вопрос собраться, спроектировать простой девайс и заняться освоением инструментов.

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

По этому пока больше оперирую опытом 8051 и Z80 (хотя не думаю, что с AVR будут серьезные отличия).

Я по этой причине принципиально использую только С - пусть и не всегда максимально эффективно (хотя кого это сегодня волнует? Если ресурсов не хватает, чуть доплачиваем и ставим более мощный кристалл, а то и два, цены вполне позволяют), зато переносимо. Например, год назад у Atmel были серьезные проблемы, настолько серьезные, что появилась мысль переходить на кристаллы конкурентов. Все, что зависит от конкретного оборудования, выносится в тонкий слой HAL, а вся остальная логика программы работает через вызовы HAL. В крайнем случае его и переписать несложно. Писать сегодня что-то серьезное на ассемблере - это весьма серьезный риск сработать в корзину.

Смотрю в статье figure 1: radio_wake_eventhandler(). На мой взгляд, эта функция вызывается из цикла типа

Код: (ASM)
loop
    halt
    /*
        Проверка событий по регистрам устройств или переменным, установленным обработчиками прерываний.
        По итогам проверок вызываются различные функции типа radio_wake_eventhandler().
    */
    jmp loop

В AVR такой же метод работы?

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

Планировщик крутится в бесконечном цикле, вызывая протопотоки по очереди. Если протопотоку нечего делать в данный момент (например, он ждет события, которое никак не произойдет), он просто возвращает управление (как в примере из Figure 4):

Код: (C)
...
case 13:
  if(!(communication_complete() || timer_expired(&timer))) {
    return;
  }
...
Если построить программу в соответствии с уже знакомым нам по прошлым статьям паттерном MCH (Model-Conductor-Hardware), то функции, определяющие факт наступления событий (communication_complete() и timer_expired(&timer)), будут реализованы в Модели и окажутся слабо связаны с конкретным оборудованием.

Поэтому идеи должны достаточно хорошо работать и с AVR, и с PIC, и с клонами X51... А если писать достаточно аккуратно, то очень большую часть кода удастся выполнить и на PC (именно поэтому для AVR я выбрал GCC, хотя есть компиляторы и поэффективнее, но заточенные только под одну архитектуру).

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

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

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

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

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

WWW
« Ответ #97 : 18-07-2011 20:15 » 

Насчет halt - это у меня все риалтаймовские замашки: таймер задает квант для конечного автомата, а тот проверяет состояние входных портов и задает выходные. Не так просто отойти от привычного. Правда, тогда периферии почти не было - пару таймеров и UART.
Записан

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

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

WWW
« Ответ #98 : 18-07-2011 20:35 » 

Насчет halt - это у меня все риалтаймовские замашки

В принципе и на AVR останов может пригодиться в двух случаях: если мы делаем мобильную конструкцию на батареях и хотим снизить потребление, либо если мы используем встроенный АЦП, в этом случае останов ядра МК существенно снижает уровень помех. В остальных случаях с остановом лучше не связываться, пусть уж лучше в пустом цикле крутится.

И еще следует добавить для полной ясности:

Код: (ASM)
loop
    halt
    /*
        Проверка событий по регистрам устройств или переменным, установленным обработчиками прерываний.
        По итогам проверок вызываются различные функции типа radio_wake_eventhandler().
    */
    jmp loop

По поводу останова уже обсудили, теперь по поводу бесконечного цикла (он же планировщик протопотоков). Функции типа radio_wake_thread(), то есть протопотоки, вызываются из него безусловно, без всяких проверок. Они сами делают все необходимые проверки интересующих их событий и, если эти события не наступили, возвращают управление обратно планировщику. Планировщик очень прост, негде наделать ошибок.
Записан

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

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

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

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

WWW
« Ответ #99 : 18-07-2011 20:56 » 

Насчет блокировок: ведь они возможны только в функции потока и не реализуемы в других функциях. Я так понимаю, считается, что любая функция, которая рассчитывает на блокировки, должна быть функцией еще одного потока и, соотв., вызываться из родительского посредством PT_SPAWN.
Записан

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

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

WWW
« Ответ #100 : 18-07-2011 21:48 » 

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

Вот что они говорят по поводу блокировок:

Цитата
Using protothreads, however, potentially blocking statements are explicitly implemented with a PT_WAIT statement.
...
A protothread may call normal C functions, but cannot block inside a called function.

Как мы уже видели, "блокировка" PT_WAIT на самом деле реализована как проверка ожидаемого условия и возврат в цикл планировщика, если условие не выполнено. Блокировать протопоток в вызываемой им функции нельзя - не сработает машина Даффа, можно только в самом потоке.
Записан

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

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

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

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

WWW
« Ответ #101 : 18-07-2011 21:58 » 

Код: (C)
PT_THREAD(thread1(struct pt *pt))
{
  static struct pt local_pt;

  PT_BEGIN(pt);

  PT_SPAWN(pt, local_pt, thread2(local_pt));

  PT_END(pt);
}

PT_THREAD(thread2(struct pt *pt))
{
  PT_BEGIN(pt);

  PT_WAIT_UNTIL(pt, check_some_event());

  PT_END(pt);
}

Вот это я имел в виду. В таком случае в thread2 возможны блокировки.


Добавлено через 3 минуты и 23 секунды:
Дока мне не понравилась - лишь отсылки к исходникам. Непосредственно код protothreads лучше рассказывает о механизмах. Благо он компактный.

Я отсюда брал исходники: http://www.sics.se/~adam/download/?f=pt-1.4.tar.gz
В архиве также вся документация приложена.
« Последнее редактирование: 19-07-2011 07:11 от RXL » Записан

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

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

WWW
« Ответ #102 : 03-08-2011 20:18 » 

Еще раз о Protothreads.
Там поддерживаются сопрограммы как через switch/case, так и через метки и goto. Второй вариант поддерживается только для GCC. Чтобы использовать его, нужно определить макрос:

Код: (C)
#define LC_INCLUDE "lc-addrlabels.h"

В результате на такую вот строчку кода

Код: (C)
    PT_WAIT_WHILE(pt, ready_flag);

препроцессор содает вот такой код:

Код: (C)
    do { do { LC_LABEL84: (((pt))->lc) = &&LC_LABEL84; } while(0); if(!(!(ready_flag))) { return 0; } } while(0);

Мне не знаком префикс && - разве что как "адрес адреса". Не совсем понятен смысл.
Еще меня здесь смутило то, что присвоение pt->lc нужно в данном коду лишь однажды, а тут выполняется при каждом переходе на метку LC_LABEL84. Немного модифицировал макрос LC_SET:

Код: (C)
/* Это оригинальный макрос
#define LC_SET(s)                               \
  do {                                          \
    LC_CONCAT(LC_LABEL, __LINE__):              \
    (s) = &&LC_CONCAT(LC_LABEL, __LINE__);      \
  } while(0)
*/


// Это мой
#define LC_SET(s)                               \
  do {                                          \
    (s) = &&LC_CONCAT(LC_LABEL, __LINE__);      \
    { LC_CONCAT(LC_LABEL, __LINE__):; }            \
  } while(0)

Навороченность объясняется необходимость располагать метку первым элементом строки или блока.
В результате код выглядит так:

Код: (C)
    do { do { (((pt))->lc) = &&LC_LABEL84; { LC_LABEL84:; } } while(0); if(!(!(ready_flag))) { return 0; } } while(0);

Проверил - поведение тестовой программы не изменилось.
« Последнее редактирование: 03-08-2011 20:20 от RXL » Записан

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

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

WWW
« Ответ #103 : 04-08-2011 06:48 » 

Мне не знаком префикс && - разве что как "адрес адреса". Не совсем понятен смысл.

Цитата
3.2.8 The Label Address Operator

As a GNU C extension, you can also take the address of a label with the label address operator &&. The result is a void* pointer which can be used with goto.

Цитата
4.10 The goto Statement

...
As an extension, GCC allows a goto statement to jump to an address specified by a void* variable. To make this work, you also need to take the address of a label by using the unary operator && (not &). Here is a contrived example:
Код: (C)
enum Play { ROCK=0, PAPER=1, SCISSORS=2 };
enum Result { WIN, LOSE, DRAW };

static enum Result turn (void)
{
  const void * const jumptable[] = {&&rock, &&paper, &&scissors};
  enum Play opp; /* opponent’s play */
  goto *jumptable[select_option (&opp)];
rock:
  return opp == ROCK ? DRAW : (opp == PAPER ? LOSE : WIN);
paper:
  return opp == ROCK ? WIN : (opp == PAPER ? DRAW : LOSE);
scissors:
  return opp == ROCK ? LOSE : (opp == PAPER ? WIN : DRAW);
}

Взято из: Trevis J. Rothwell, The GNU C Reference Manual.
http://www.gnu.org/software/gnu-c-manual/gnu-c-manual.pdf
Записан

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

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

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

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

WWW
« Ответ #104 : 04-08-2011 07:08 » 

Спасибо за пояснение. Чувствовал, что что-то нестандартное используется.
Записан

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

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

WWW
« Ответ #105 : 04-08-2011 07:29 » 

Спасибо за пояснение. Чувствовал, что что-то нестандартное используется.

Конечно, неспроста ведь

Второй вариант поддерживается только для GCC.

Другие компиляторы попросту не поймут этой конструкции, для них годится только ANSI-совместимая "машина Даффа".
Записан

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

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

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

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

WWW
« Ответ #106 : 04-08-2011 07:32 » 

Про специфику GCC я первым делом подумал про конкатенацию в макросах - "##" - ее в VC нет.

Вариант на switch явно избыточен по коду за счет множества case.
Записан

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

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

WWW
« Ответ #107 : 04-08-2011 07:51 » 

Про специфику GCC я первым делом подумал про конкатенацию в макросах - "##" - ее в VC нет.

Это как раз стандартная конструкция препроцессора:

Цитата
Оператор ## позволяет в макрорасширениях конкатенировать аргументы. Если в замещающем тексте параметр соседствует с ##, то он заменяется соответствующим ему аргументом, а оператор ## и окружающие его символы-разделители выбрасываются. Например, в макроопределении paste конкатенируются два аргумента
Код: (C)
#define paste(front, back) front ## back
так что paste(name, 1) сгенерирует имя name1.
(Керниган, Ритчи. Язык программирования C. Гл. 4.11)

В MSDN для VC тоже описывается:

Цитата
Token-Pasting Operator (##)

The double-number-sign or "token-pasting" operator (##), which is sometimes called the "merging" operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token and therefore cannot be the first or last token in the macro definition.

« Последнее редактирование: 04-08-2011 07:53 от Dale » Записан

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

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

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

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

WWW
« Ответ #108 : 04-08-2011 08:09 » 

Век живи - век учись...
Записан

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

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


« Ответ #109 : 04-08-2011 15:06 » 

Про специфику GCC я первым делом подумал про конкатенацию в макросах - "##" - ее в VC нет.

если VC - это студия, то ещё как есть! Я даже в шестёрке этот оператор применял
Записан

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

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

WWW
« Ответ #110 : 04-08-2011 15:45 » 

Леш, я ж говорю:

Век живи - век учись...

Улыбаюсь

MSVS последние 5 лет я использовал только для VB6.
Записан

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

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


« Ответ #111 : 04-08-2011 16:42 » 

ну, тады на всякий случай, вдруг тоже незнакомо, но удобно бывает:

оператор # в макросе - оборачивает параметр в кавычки (не значение параметра, а именно выражение , поставленное в скобки)

Код:
 #define def_quoted_param(text) #text
#define def_quoted_param_L(text) L#text

const char*  p1=def_quoted_param  (123abc);
const WCHAR* p2=def_quoted_param_L(456qwe);

Записан

Страниц: 1 2 3 [4]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines