Задача: отслеживается событие и при его возникновении обрабатывается.
Решение:
while (1) {
if ( <событие> ) {
<обработка>
}
}
Проблема: много холостых ходов цикла, загрузка процессора до 100%В Windows решается просто:
while (1) {
if ( <событие> ) {
<обработка>
continue;
}
else {
Sleep (0);
}
}
здесь функция Sleep(0) позволяет планировщику передать исполнение другому потоку до истечения кванта времени выделенного текущему потоку.Вопрос: Как подобный механизм реализовать в UNIX ?
> Решение:
> while (1) {
> if ( <событие> ) {
> <обработка>
> }
> }
> Проблема: много холостых ходов цикла, загрузка процессора до 100%Решение не совсем правильное. (Я, кстати, очень сильно удивлюсь если в Виндовсе при использовании Sleep() загрузка процессора будет не 100%).
Правильно - использовать select() (man 2 select) или poll() (man 2 poll), что-то типа
while (select(...)) {
// event happened. Process it:
...
}при этом внутри цикла нет ветки else со sleep() - select() сам ждет события, и поскольку он реализован через системный вызов, кернел знает что данный поток будет не активным пока не произойдет событие заданное параметрами select()'а, и может наиболее эффективно распорядится ресурсами системы - например, поток не будет активизирован просто чтобы убедится что событие еще не наступило и сказать sleep() в очередной раз.
select()/poll() работают ожидая события ввода/вывода на файловых дескрипторах (наличия данных для чтения, готовности к записи, возникновения ошибки), но это на самом деле не является существенным ограничением:
а) если событие которого вы ждете это событие ввода/вывода на файловом дескрипторе к которому вы имеете доступ, оно прямо транслируется в параметеры select()/poll();
б) если это какое-то иное событие, значит где-то в системе есть поток который висит на вызове функции, завершение которой и будет сигналом этого события (напр. ожидание завершения вызова функции интерфейса какой-то базы данных, или ожидание ответа от пользователя в потоке графического интерфейса, и т.д). В этом случае довольно легко организовать пару файловых дескрипторов (напр. функцией pipe() (man pipe)); поток который ожидает события в цикле while(select()) на самом деле ожидает наличия данных на выходном конце pipe'а, а поток который дождался завершения вызова функции, поставляет эти данные - пишет, например, всего 1 байт во входной конец pipe'а, после чего select() в другом потоке завершается и тело цикла while выполняется.
А кстати, man sched_yield чтобы почитать о функции которая отдает квант выделеный текущему потоку, хотя по-моему это не то что тут требуется.
> которая отдает квант выделенный текущему потокуНичё она не отдаёт, а говорит планировщику "елси нада, то могёшь меня переместить в конец очереди".
Более того, поведение родственных нитей зависит от типа приоритета: OTHER, BATCH, IDLE, FIFO, RR, DEADLINE;
C FIFOй мож ваще не переключится, а тормознёт, планировщик увидит, что у тя самый большой приоритет и вернёт
обратно на первое место к процессору.
> Вопрос: Как подобный механизм реализовать в UNIX ?sched_yield()
---
И ваще, планировщики и проблемы разруливания процессов/нитей - это задачи
системного программирования, а не прикладного.
> И ваще, планировщики и проблемы разруливания процессов/нитей - это задачи
> системного программирования, а не прикладного.Точно. Именно в этом уверены все :)прикладники.
Спасибо за ответы. Я действительно немного промахнулся с темой, поищу в теме про потоки и нити.К сожалению sched_yield() не помогает в данном случае, например:
void main () {
while (1) {
}
}и
void main () {
while (1) {
sched_yield ();
}
}согласно top дают одинаковую загрузку CPU ~100%
Однако
void main () {
while (1) {
usleep (1000);
}
}резко уменьшает нагрузку до 0.3%
Наверное это и есть решение.
Привет,> if ( <событие> ) {
> <обработка>
> }Ответ зависит от того, в чем заключается обработка и точнее, насколько она должна быть в реальном времени. Лучший вариант, конечно, select() - есил есть возможность его использовать. usleep() позволит снизить нагрузку, а интервал зависит от того, как часто случается событие и насколько его обработка может подождать - напр., если "событие", это уборка за каким-то "мусором" (типа wait() после fork()), то и раз в секунду подойдет.
WWell,
>[оверквотинг удален]
>> if ( <событие> ) {
>> <обработка>
>> }
> Ответ зависит от того, в чем заключается обработка и точнее, насколько она
> должна быть в реальном времени. Лучший вариант, конечно, select() - есил
> есть возможность его использовать. usleep() позволит снизить нагрузку, а интервал зависит
> от того, как часто случается событие и насколько его обработка может
> подождать - напр., если "событие", это уборка за каким-то "мусором" (типа
> wait() после fork()), то и раз в секунду подойдет.
> WWell,Доброго времени суток!
Реальная задача в получении пакетов на неблокированные сокеты:
while (1) {
. . .
if ( !poll(. . .) ) {
continue;
}<обработка>
}
select() не пробовал, но использование poll(), как в приведённом примере, оставляет загрузку CPU ~100%.
Привет,> Реальная задача в получении пакетов на неблокированные сокеты:
> select() не пробовал, но использование poll(), как в приведённом примере, оставляет загрузку
> CPU ~100%.Тогда попробуйте select(), он и есть ваше решение.
WWell,
> select() не пробовал, но использование poll(), как в приведённом примере, оставляет загрузку
> CPU ~100%.Если загрузка 100%, то timeout=0. Ну уж тогда извините, что написали - то и получили.
select() не поможет, так как на уровне ядра всё равно реализован через poll().
>[оверквотинг удален]
> <обработка>
> continue;
> }
> else {
> Sleep (0);
> }
> }
> здесь функция Sleep(0) позволяет планировщику передать исполнение другому потоку до истечения
> кванта времени выделенного текущему потоку.
> Вопрос: Как подобный механизм реализовать в UNIX ?То что ты делаешь называется опрос. И это именно то, чего делать не стоит. Для этого алгоритма избежать 100% загрузки цпу невозможно. Вариант с usleep - костыль, который резко уменьшает время ответа системы.
Само получение ивента должно быть блокируемым. Самое простое решение - завести дескриптор при помощи man 2 pipe и с одной стороны - писать, а с другой - читать в блокирующем режиме ивенты.
>[оверквотинг удален]
>> }
>> здесь функция Sleep(0) позволяет планировщику передать исполнение другому потоку до истечения
>> кванта времени выделенного текущему потоку.
>> Вопрос: Как подобный механизм реализовать в UNIX ?
> То что ты делаешь называется опрос. И это именно то, чего делать
> не стоит. Для этого алгоритма избежать 100% загрузки цпу невозможно. Вариант
> с usleep - костыль, который резко уменьшает время ответа системы.
> Само получение ивента должно быть блокируемым. Самое простое решение - завести дескриптор
> при помощи man 2 pipe и с одной стороны - писать,
> а с другой - читать в блокирующем режиме ивенты.Зря, что ли, придумывали асинхронно-событийную парадигму?
> Зря, что ли, придумывали асинхронно-событийную парадигму?А не из теории что-нибудь?!
>> Зря, что ли, придумывали асинхронно-событийную парадигму?
> А не из теории что-нибудь?!http://libevent.org/ || http://software.schmorp.de/pkg/libev.html