В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус.
Т.е. что-бы именно основной поток заходил в мою функцию, а не другой поток.
Можно ли указать участок коды выполнение которого нельзя прерывать ? Т.е. что-бы при заполнении струкруры данных, она либо полностью была заполнена либо ещё не тронута.
В линукс прерывание по таймеру можно реализовать через сигналы.
signal(SIGALRM, sighandler); // Устанавливаем обработчик прерывания
alarm(5); // Задаём интервал 5 секунд
Как это можно сделать в Windows или кроссплатформенно ?
Здравствуйте, maks1180, Вы писали:
M>что-бы именно основной поток заходил в мою функцию, а не другой поток. M>Можно ли указать участок коды выполнение которого нельзя прерывать ?
В WinAPI вообще нет средств, позволяющих пользовательскому коду произвольно прерывать выполнение пользовательского же потока. Такое применяется в однопоточных системах, где невозможно организовать естественный параллелизм. В WinAPI переключение потока на пользовательский код возможно лишь в alertable-состоянии, в которое поток может войти только явно (например, через SleepEx и подобные функции).
Произвольно прерывать поток может только ядерный код.
ЕМ>Произвольно прерывать поток может только ядерный код.
Ну да, ядерный код, должен в определённое время прервать определённый поток и передать выполнение заранее определённой функции.
Осталось найти WinAPI функции, что-бы сообщить это ядру.
Здравствуйте, maks1180, Вы писали:
M>Почему хорошо ?
Потому, что поддержка прерываний еще и на уровне пользовательского кода — это дополнительный геморрой по хранению правил переходов, текущих состояний, предоставлению примитивов по управлению всем этим, а для чего? Повторю: эти техники используются от бедности отсутствия нормальной, человеческой многопоточности, которая позволяет коду автоматически масштабироваться между одним процессором и бесконечностью их.
ЕМ>>Я же сказал — ее не существует. И это очень хорошо. M>Почему хорошо ?
Ну вот вы аппелируете к сигналам, а вы в курсе, что список функций libc, которыми может пользоваться обработчик сигнала крайне ограничен? Даже printf'ом нельзя пользоваться, не говоря уж о malloc'е. А если оно у вас работает, то это благодаря высокому уровню удачи. Реально самый адекватный способ работы с сигналами — это блокировать их все нахрен, и крутить sigwait или же signalfd + select/poll. То есть по сути — аналог alertable wait.
Как много веселых ребят, и все делают велосипед...
ЕМ>В WinAPI вообще нет средств, позволяющих пользовательскому коду произвольно прерывать выполнение пользовательского же потока. Такое применяется в однопоточных системах, где невозможно организовать естественный параллелизм. В WinAPI переключение потока на пользовательский код возможно лишь в alertable-состоянии, в которое поток может войти только явно (например, через SleepEx и подобные функции).
будет ли поток в alertable-состоянии при нахождении внутри следующих функций ?
1) блокирующие функции WriteFile или ReadFile
2) получения списка файлов. Кажется FindNextFile.
O>Ну вот вы аппелируете к сигналам, а вы в курсе, что список функций libc, которыми может пользоваться обработчик сигнала крайне ограничен? Даже printf'ом нельзя пользоваться, не говоря уж о malloc'е.
Нет не в курсе. malloc ещё понятно, не понятно почему printf'ом нельзя ?
O>>Ну вот вы аппелируете к сигналам, а вы в курсе, что список функций libc, которыми может пользоваться обработчик сигнала крайне ограничен? Даже printf'ом нельзя пользоваться, не говоря уж о malloc'е. M>Нет не в курсе. malloc ещё понятно, не понятно почему printf'ом нельзя ?
Но там же по ссылке в третьем абзаце вот прямо на примере printf-а расписано почему
Как много веселых ребят, и все делают велосипед...
Здравствуйте, maks1180, Вы писали:
M>будет ли поток в alertable-состоянии при нахождении внутри следующих функций ? M>1) блокирующие функции WriteFile или ReadFile
Нет. Можете использовать неблокирующие, за которыми сразу же идут функции alertable wait.
M>2) получения списка файлов. Кажется FindNextFile.
Разумеется, нет. С чего бы? Поток находится в состоянии alertable исключительно во время ожидания.
Я когда-то начинал делать асинхронные процессы и события на платформах, где нет многопоточности, и отсутствие прерываний в user-mode меня поначалу тоже удивляло. Но многопоточность позволяет все это делать в разы проще и надежнее, а главное — естественным образом ложится на современные многопроцессорные архитектуры. Делать что-то на средствах вроде сигналов сейчас имеет смысл исключительно с целью переноса на недоплатформы.
Здравствуйте, maks1180, Вы писали:
M>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. M>Т.е. что-бы именно основной поток заходил в мою функцию, а не другой поток.
Откуда этот статус будет браться?
Почему именно в одном потоке это нужно?
Что делает основной поток, там в цикле что-то делается?
Опиши задачу целиком, может быть оптимально использовать какой-то другой механизм.
O>Но там же по ссылке в третьем абзаце вот прямо на примере printf-а расписано почему
1) Да, прочитал. На эту тему у меня тоже был вопрос:
Можно ли указать участок коды выполнение которого нельзя прерывать ? Т.е. что-бы при заполнении струкруры данных, она либо полностью была заполнена либо ещё не тронута ?
2) Если выделить память в стеке, вызвать sprintf, потом получившуюся строку передать в write() так можно ?
Здравствуйте, maks1180, Вы писали:
M>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус.
А зачем для этого прерывать поток, что мешает завести еще один поток?
M>Т.е. что-бы именно основной поток заходил в мою функцию, а не другой поток.
распихайте по коду вызов функции yield() или еще как назовите.
и внутри неё вызывайте если что-то накопилось.
M>Можно ли указать участок коды выполнение которого нельзя прерывать? Т.е. что-бы при заполнении струкруры данных, она либо полностью была заполнена либо ещё не тронута.
M>В линукс прерывание по таймеру можно реализовать через сигналы. M> signal(SIGALRM, sighandler); // Устанавливаем обработчик прерывания M> alarm(5); // Задаём интервал 5 секунд
M>Как это можно сделать в Windows или кроссплатформенно ?
сделайте машины состояния и их можно будет итерировать в любом порядке
M>1) Да, прочитал. На эту тему у меня тоже был вопрос: M>Можно ли указать участок коды выполнение которого нельзя прерывать ? Т.е. что-бы при заполнении струкруры данных, она либо полностью была заполнена либо ещё не тронута ?
Да, sigprocmask — но это будет изобретением синхронизации на коленке.
M>2) Если выделить память в стеке, вызвать sprintf, потом получившуюся строку передать в write() так можно ?
sprintf в списке по ссылке выше нету, так что лично я бы так не делал. write(1,.. ) — это выход, да. Если есть готовая строка.
Приведу пример из жизни. Есть у нас комплекс хардварно-софтварных автотестов, который измеряет в том числе общую производительность системы. Одним из тестов который он делает является вот этот нехитрый тулкит. Не невесть что, но адекватных попугаев выдает. Но была на заре становления комплекса с ним одна беда — раз в неделю..две зависал рандомный билд, в ходе исполнения этого теста. Когда надоело, и начали разбираться, оказалось что эта тулза делает как раз примерно что делаете вы: alarm и fprintf(stderr, и вот последний зависал, так как оказался вызванным из кишок какого другого printf.
Тулкит мы у себя переделали на перевод числа в строку врукопашную + write и проблема ушла.
Как много веселых ребят, и все делают велосипед...
M>>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. _>А зачем для этого прерывать поток, что мешает завести еще один поток?
Мешает, то что нужно будет делать синхронизацию данных и дополнительные блокировки, что снизит скорость.
M>>Т.е. что-бы именно основной поток заходил в мою функцию, а не другой поток. _>распихайте по коду вызов функции yield() или еще как назовите. _>и внутри неё вызывайте если что-то накопилось.
От безысходности, наверно придёться так делать.
Но тут если часто проверять нужно ли вызывать yield(), буду терять производительность. Если редко, то непонятно насколько реже нужно его вызывать, что-бы получить приемлемый результат.
В каждом курсе кода нужно будет мерить как часто проверять время, не пора ли вызвать yield().
M>>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. M>>Т.е. что-бы именно основной поток заходил в мою функцию, а не другой поток.
AN>Откуда этот статус будет браться?
Из глобальных переменных.
AN>Почему именно в одном потоке это нужно?
Что-бы избежать синхронизации между потоками.
AN>Что делает основной поток, там в цикле что-то делается?
Да, считает хэш сумму файлов и копирует файлы при необходимости.
AN>Опиши задачу целиком, может быть оптимально использовать какой-то другой механизм.
Пока я хочу понять если ли такая возможность в Windows. Получается, что в Linux есть, а в Windows нет ?
AN>>Почему именно в одном потоке это нужно? M>Что-бы избежать синхронизации между потоками.
Но но с сигналами надо тоже ее по сути делать — через sigprocmask и не факт что это будет быстрее. Точнее это будет точно медленнее чем обращение одной переменной через __sync_* чего достаточно судя по описанию проблемы.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, maks1180, Вы писали:
M>>>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. _>>А зачем для этого прерывать поток, что мешает завести еще один поток?
M>Мешает, то что нужно будет делать синхронизацию данных и дополнительные блокировки, что снизит скорость.
Это собственно говоря почему?
Вопервых надо уведомлять о прогрессе не постоянно, а после группы проделанных операций или с задержкой (например не чаще 10 раз в сек)
Во вторых вывод прогресса это не столь ресурсоёмкая часть.
M>От безысходности, наверно придёться так делать. M>Но тут если часто проверять нужно ли вызывать yield(), буду терять производительность. Если редко, то непонятно насколько реже нужно его вызывать, что-бы получить приемлемый результат.
Что бы было быстро надо выполнять действия не по одному, а партиями по 2-3 млн шт
M>В каждом курсе кода нужно будет мерить как часто проверять время, не пора ли вызвать yield().
Вообще-то можно сделать предварительный делитель например
Здравствуйте, Евгений Музыченко, Вы писали:
M>>что-бы именно основной поток заходил в мою функцию, а не другой поток. M>>Можно ли указать участок коды выполнение которого нельзя прерывать ? ЕМ>В WinAPI вообще нет средств, позволяющих пользовательскому коду произвольно прерывать выполнение пользовательского же потока.
Существует: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
Ты немного не прав. APC начинают обрабатываться только при переходе в alertable-состояние (Sleep* & Wait*). Т.е., как раз произвольно прервать выполнение кода APC не позволяет.
Здравствуйте, maks1180, Вы писали:
M>Как это можно сделать в Windows или кроссплатформенно ?
Правильно это делается заведением отдельного потока для выполнения этой работы. Основной при этом ожидает завершения работы, периодически просыпаясь — WaitForSingleObjectEx с тайм-аутом внутри бесконечного цикле. Отработал тайм-аутов, обновляем вывод из разделяемой с потоком памяти, вышли не по тайм-ауту — рабочий поток завершился, выходим из цикла и продолжаем работу основного потока. В Linux есть аналогичные примитивы с pthread.
1. Заводишь общую память, в которую будешь записывать статус.
2. В рабочем потоке есть отдельная локальная память где постоянно обновляется статус без синхронизации.
3. С определённой периодичностью (например раз в 1000 циклов) делаешь синхронизацию и копируешь в общую память данные из локальной памяти). Т.к. это происходит редко, затраты на синхронизацию небольшие.
4. В отдельном потоке с синхронизацией периодически читаешь из общей памяти статус и выводишь куда положено.
Здравствуйте, maks1180, Вы писали:
M>2)Если выделить память в стеке, вызвать sprintf, потом получившуюся строку передать в write() так можно ?
Если write действительно реализовано, как signal-safe, то можно. sprintf обычно тоже не использует глобальных переменных, но могут быть исключения при обработке плавучки.
Здравствуйте, maks1180, Вы писали:
M>то что нужно будет делать синхронизацию данных и дополнительные блокировки, что снизит скорость.
Если делать грамотно, то не снизит. А синхронизацию Вам придется делать в любом случае — хотя бы запрет прерывания кода там, где оно поддерживается.
M>если часто проверять нужно ли вызывать yield(), буду терять производительность.
Вы точно уверены, что будете ее терять, или это лишь так кажется? В какое количество микросекунд должен укладываться код без вызовов yield?
Здравствуйте, Евгений Музыченко, Вы писали:
_>>сделайте машины состояния
ЕМ>По-русски это называется "конечный автомат".
finite state machine — конечный автомат
а тут может быть не не конечный
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>По барабану.
Ага это если эта машина замкнутая и все состояния хранятся внутри неё, а если она выполняет запросы во внешнюю среду?
O>>sprintf в списке по ссылке выше нету, так что лично я бы так не делал. ЕМ>Если без плавучки, то не могу представить ситуации, в которой он пользовался бы глобальными переменными.
Достаточно обращения к TLS (с возможной ленивой (пере)инициализацией или malloc'а внутри для получения гейзенбага. Да, скорее всего там его нету, но достаточно взгляда на исходники glibc чтобы передумать их изучать и просто следовать документации. Не говоря о том что glibc — лишь одна из реализаций, есть еще uclibc, musl libc, bionic libc, BSD libc.. Благо, апи libc — одна из немногих адекватно документированных вещей в опенсурсе.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, maks1180, Вы писали:
M>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. M>Как это можно сделать в Windows или кроссплатформенно ?
Через Waitable Timer (WinAPI)? Не пробовал, но похоже на то, что вам нужно.
Здравствуйте, kov_serg, Вы писали:
_>Ага это если эта машина замкнутая и все состояния хранятся внутри неё, а если она выполняет запросы во внешнюю среду?
Какая еще "внешняя среда"? Конечный автомат — термин из математики, у него вообще нет никакой "среды".
Здравствуйте, Maniacal, Вы писали:
M>Здравствуйте, maks1180, Вы писали:
M>>В консольном приложение нужно прерывать выполнение кода регулярно, скажем 1 раз в секунду и вызывать свою функцию что-бы выводить статус. M>>Как это можно сделать в Windows или кроссплатформенно ?
M>Через Waitable Timer (WinAPI)? Не пробовал, но похоже на то, что вам нужно.
The completion routine will be executed by the same thread that called SetWaitableTimer. This thread must be in an alertable state to execute the completion routine. It accomplishes this by calling the SleepEx function, which is an alertable function.
Т.е. тред должен явно освободиться и уйти в ждущее состояние (читай — надо закодить руками).
MD>The completion routine will be executed by the same thread that called SetWaitableTimer. This thread must be in an alertable state to execute the completion routine. It accomplishes this by calling the SleepEx function, which is an alertable function.
MD>Т.е. тред должен явно освободиться и уйти в ждущее состояние (читай — надо закодить руками).
Попробовал, да, действительно, без вызова какой-либо alertable function таймер не срабатывает.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Какая еще "внешняя среда"? Конечный автомат — термин из математики, у него вообще нет никакой "среды".
Внешняя по отношению к исполнителю, конечного автомата. Например последовательный порт, микрофон, видеопоток, сеть и т.п.
То что есть сферические кони в ваккуме это да, но реальность немного сложнее.
Здравствуйте, maks1180, Вы писали:
AN>>Что делает основной поток, там в цикле что-то делается? M>Да, считает хэш сумму файлов и копирует файлы при необходимости.
Для чтения и записи файлов можно сделать event loop с ожиданием событий от файловых дескрипторов и таймера. Для кроссплатформенного решения можно libevent использовать.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А если Вам доведется, например, вычислять факториал числа, полученного из внешних данных, Вы тоже придумаете отдельный термин вместо "факториала"?
Что-то вас не терминах прям коротит. Причем тут факториал?
Pzz>Потому, что printf пишет в FILE*, а работа с FILE* может требовать синхронизации, а ее для обработчиков сигналов не предусмотрено.
Я тоже так подумал, но мне кажеться что printf должен писать сразу через write(), т.е. без буферизации. Иначе, часть текста не сразу выведется на консоль, а только при следующем вызове printf.
Такое поведение не допустимо.
AN>Для чтения и записи файлов можно сделать event loop с ожиданием событий от файловых дескрипторов и таймера. Для кроссплатформенного решения можно libevent использовать.
libevent наверно работает так же как и libuv ?
Если да, то нет смылса, он создаёт дополнительный поток + синхронизация с ним.
Здравствуйте, kov_serg, Вы писали:
_>Что-то вас не терминах прям коротит.
Да, меня раздражает, когда люди, которые вроде как специалисты, ломают устоявшуюся терминологию без веских причин.
_>Причем тут факториал?
При том, что в науках и связанных с ними дисциплинах для обозначения большинства сущностей используются определенные термины, за счет чего обеспечивается единство "понятийного пространства".
_>Сколько состояний у телеграфного ключа?
Вы бы лучше посмотрели, как выглядит ссылка на эту Вашу картинку. Бездумно копипастить ссылки — плохая привычка, а в ряде случаев — еще и опасная.
Здравствуйте, maks1180, Вы писали:
M>libevent наверно работает так же как и libuv ? M>Если да, то нет смылса, он создаёт дополнительный поток + синхронизация с ним.
Дополнительный поток не создаётся. callback-функции вызываются в том же потоке, в котором ожидаются события от файловых дескрипторов.
Здравствуйте, maks1180, Вы писали:
M>В линукс прерывание по таймеру можно реализовать через сигналы. M> signal(SIGALRM, sighandler); // Устанавливаем обработчик прерывания M> alarm(5); // Задаём интервал 5 секунд
M>Как это можно сделать в Windows или кроссплатформенно ?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Вы бы лучше посмотрели, как выглядит ссылка на эту Вашу картинку. Бездумно копипастить ссылки — плохая привычка, а в ряде случаев — еще и опасная.
Вы бы лучше бы изучили матчасть. Обычная ссылка, встраивающая изображение прямо в html, закодированное в base64. Ничего опасного, вполне стандатный подход.