LINUX.ORG.RU

Как правильно?

 ,


0

1

Я опять пытаюсь что-то делать на плюсах. Так, ущербные хеллоуворлды. И сейчас пытаюсь понять одну вещь.

Допустим, есть несколько функций, которые должны выполняться (в бесконечном цикле) одновременно параллельно, не дожидаясь завершения выполнения других функций. Некоторые из них содержат Sleep. Начал гуглить про std::async, а в MinGW (хочу в кроссплатформу, поэтому gcc (или g++, чо там вообще)) это не реализовано.

В общем, такой вопрос: как сделать подобное правильно, чтобы ещё и кроссплатформенно?

UPD: std::async заработал, но функции всё ещё перестают выполняться, пока выполняется функция со Sleep.

UPD2: Sleep нужен для создания задержек. Т.е. в функции произошло одно действие, затем Sleep(1000), затем другое действие.

UPD3: с std::this_thread::sleep_for(std::chrono::seconds(1)); то же самое.

UPD4: с std::thread и thread.detach() функции выполняются какое-то время, а потом перестают выполняться вообще.

Deleted

Последнее исправление: Deleted (всего исправлений: 7)

std::thread

Однако тут надо понять, что это у тебя за функции такие и надо ли им действительно выполняться одновременно (и что ты понимаешь под «одновременно»)

yoghurt ★★★★★
()

Sleep скорее всего не нужен в этих функциях. Реальных юзекейсов, когда нужен безусловный сон даже сложно сходу придумать. Миллисекундные задержки ещё могут быть частью какого-то протокола связи с железом по uart тому же, но опять же проще в асинхронный ввод/вывод чем Sleep. Тем более, это крайне не точный сон, погрешность в 20ms довольно легко получить, а если система загружена, то и выше.

pon4ik ★★★★★
()
Ответ на: комментарий от yoghurt

Ну там как бы не одновременно, а параллельно. Но типа чтобы функции не выполнялись последовательно.

Deleted
()
Ответ на: комментарий от Deleted

А это зависит от того, зачем тебе задержка. Например можно ожидать на select'e или на условной переменной.

Прежде чем делать какие то многопоточные изыскания, крайне рекомендую хотя бы в общих чертах ознакомиться с планировщиками, например это доступно изложено в первых двух главах книги Таненбаума «Современные операционные системы».

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Sleep скорее всего не нужен в этих функциях. Реальных юзекейсов, когда нужен безусловный сон даже сложно сходу придумать.

От пинга до мигания курсора (и прочей анимации) и завершения какой-либо функции по таймауту. Или всё это нынче без sleep'а делается?

monk ★★★★★
()
Ответ на: комментарий от monk

Ну, если в пинг я ещё поверю, то мигание курсора точно нужно делать на таймерах. Если покажешь мне ПО где под мигание курсора заведён отдельный поток, я сам похохочу, и коллегам дам поржать.

pon4ik ★★★★★
()
Ответ на: комментарий от monk

завершения какой-либо функции по таймауту.

Не хотел бы я с тобой оказаться тёмной ночью на одном проекте :)

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

А это зависит от того, зачем тебе задержка

Допустим, мне надо менять значение переменной раз в 0.1 секунды. Мне кстати оно правда надо, но раз в 1 секунду.

Deleted
()

Начал гуглить про std::async, а в MinGW (хочу в кроссплатформу, поэтому gcc (или g++, чо там вообще)) это не реализовано.

Убедись, что используешь сборку mingw с pthreads

annulen ★★★★★
()
Ответ на: комментарий от Deleted

Звучит как синтетическая задача. В отрыве от цикла обработки сообщений приложения и от какого то конкретного приложения.

pon4ik ★★★★★
()
Ответ на: комментарий от Deleted

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

anonymous
()
Ответ на: комментарий от pon4ik

А подсистема таймеров разве не отдельный поток (например, в ядре там отдельное IRQ)? Или подразумеваешь, что цикл событий обязан быть в любой программе, которая что-то меняет на экране?

monk ★★★★★
()
Ответ на: комментарий от monk

А подсистема таймеров разве не отдельный поток

В юзерспейсе «зависит от». Например от требуемого разрешения таймера. В gui чаще всего это часть основного цикла обработки событий. В серверных приложениях, если там не критично разрешение часто вешается на один из циклов обработки событий, например тот же asio. В ядре под процессинг любых очередей конечно же есть пулы «потоков».

Или подразумеваешь, что цикл событий обязан быть в любой программе, которая что-то меняет на экране?

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

pon4ik ★★★★★
()
Ответ на: комментарий от monk

Дабы раскрыть предыдущую мысль. Блокирующий системный вызов это уже обработка события - как минимум о готовности устройства к операции ввода/вывода. Дальше идёт ветка устройство либо не готово по истечению таймаута (которым управляет ядро) и на выходе идёт статус «ошибка» либо операция происходит и на выходе идёт статус «успех». Но на уровне ядра, это всё равно очередь задач которая разгребается по событиям от устройств ввода вывода например от диска или от таймера. Иногда даже поведением этого разгребания можно управлять из юзерспейса(например см. опцию SO_BUSY_POLL).

pon4ik ★★★★★
()
Ответ на: комментарий от annulen

Я что-то куда-то докинул, и оно заработало (хотя я не уверен, что надо было докидывать, вроде достаточно было указать линкеру -pthread), спасибо.

Сейчас смотрю, оно ли мне было нужно или нет.

Deleted
()

Если тебе поупражнятся - напиши пул потоков который разбирает очередь задач при их наличии или просто спит пока задач нет (producer/consumer). Например, к каждой букве из стандартного входа приписать tid потока который её процессит и вывести на экран). Потом сходи в сорцы boost::asio::io_service и посмотри, вдруг оно чем-то похоже оказалось. Ну или если boost инопланетно, то в QEventLoop. Хинт - в этой задаче не нужен sleep, более того, реализация с использованием sleep будет ощутимо менее эффективна чем без него.

Или например, напиши какой нить серверочек который процессит вход на select/epoll. Как промежуточное упражнение - можно простой шелл написать.

Потом, отвлекись на конечные автоматы и сделай таки консольный калькулятор, который считает выражения вида «sin(42) * 32 / (22 - (1 + acos(2)))» и выводит корректные сообщения об ошибках в выражении.

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от Deleted

Допустим, мне надо менять значение переменной раз в 0.1 секунды. Мне кстати оно правда надо, но раз в 1 секунду.

риалтайм тебе нужен, не?

anonymous
()
Ответ на: комментарий от pon4ik

А если есть кольцевой буфер, и, он пишется (пополняется кусками) за разное время (и/или размеры кусков разные), а читается всегда за одинаковое (одинаковыми кусками). Я полагаю тут нужно динамически рассчитывать длительность сна для писателя и ставить «константное» время читателю. Нет?

deep-purple ★★★★★
()
Ответ на: комментарий от Deleted

Да, ничего не надо было докидывать, всё заработало и так.

Deleted
()
Ответ на: комментарий от deep-purple

На практике буфер служит для (внезапно) буферизации каких-то i/o или вычислительных задач. В зависимости от характера данных он либо всегда содержит данные для процессинга и тогда спать не эффективно по определению.

Но чаще всего ввод вывод имеет немного другой характер, имеются пиковые нагрузки и состояние простоя. Как раз вариации на тему busy_poll являются эффективным подходом в таком случае в паре с ожиданием на cv или семафоре.

Например я видел примерно такой подход, который справлялся с задачей на ура:

  • ждём входа на cv (спать пока буфер пуст)
  • разгребаем его полностью (всё что успеваем, на то он и ring)
  • активно(не отдавая поток планировщику) ждём ещё 2us(параметр из конфига, под конкретное окружение), если он начинает заполнятся разгребаем
  • засыпаем на cv

Да, тут можно было бы попробовать вести статистику и менять этот таймаут в рантайме, вопрос сколько бы заняли эти вычисления и какова была бы их точность. Сложная статистика вполне могла бы занять дольше 2us :) А простая давать плохие результаты. Но история не про классический sleep(по сути если на родственных (affinity) ядрах крутится кто-то ещё, это команда отзаниматься им своими делами и уснуть, а потом, так уж и быть если таймаут истёк тебя разбудить), а про то, как выработать квант максимально эффективно.

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Классическая задачка. А можно подробнее про активное ожидание? Там просто спин while (currentTime < endTile) {} или можно например выполнять полезную работу из других буферов?

q0tw4 ★★★★
()
Ответ на: комментарий от q0tw4

Просто спин на на уточнённом значении rdtsc(на самом деле вкусовщина, gmtime под лялихом не сильно хуже). Конкретно в этом случае никто не пытался заняться чем то ещё, т.к. пик обработать важнее чем пытаться что-то ещё сделать. Теоретически, можно попробовать считать что-то с сильно меньшей гранулярностью, но это ведь надо ещё придумать такую полезную нагрузку и остаток такого слота всё равно придётся ждать на слипе. Можно пробовать мусор какой-то собирать наверное.

pon4ik ★★★★★
()
Ответ на: комментарий от pon4ik

Нет, я только о том, что когда буфер полон, то нет смысла напрягаться заполнять его дальше — нужно подождать пока читатель все данные «заберёт».

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Это не кольцевой буфер а очередь фиксированного размера :) Мне всегда казалось, что суть кольцевого буфера в том, что то, что не успели обработать уходит в /dev/null.

В случае очереди фиксированного размера можно ведь просто заблокироваться на записи в неё, пока не лезет. Можно делать try_write по таймауту и в промежутках делать какие то полезные дела (которые ещё нужно придумать).

pon4ik ★★★★★
()

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

Можно создать пул потоков и отдавать ему задачи (Sleep() реализуется на таймерах, а не как sleep()), можно создать несколько потоков (тогда Sleep() - это sleep()), можно реализовать что-то асинхронное (тогда поток может быть один, или пул, если нужна параллельность).

anonymous
()
Ответ на: комментарий от pon4ik

что не успели обработать уходит в /dev/null

Не всегда, вернее, не с той стороны, а со стороны читателя, особенно когда их много — если он лагает и оказался в самой жопе, что мешает писать писателю и читать другим нормальным читателям, его пинают на позицию самого быстрого. Так «/dev/null» почувствует только этот проблемный.

deep-purple ★★★★★
()
Ответ на: комментарий от pon4ik

Реальных юзекейсов, когда нужен безусловный сон даже сложно сходу придумать

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

vvviperrr ★★★★★
()
Последнее исправление: vvviperrr (всего исправлений: 2)
Ответ на: комментарий от vvviperrr

Ну я про uart как пример работы с шиной упомянул, да. Шины, да ещё и без классической оси - только спатеньки. Но как спатеньки - там разве есть прерывания таймера на stm8? Т.е. сон активный выходит?

pon4ik ★★★★★
()

UPD: std::async заработал, но всё ещё тормозит, если в функции есть Sleep.

что это значит? всмысле «тормозит»? тыж сам sleep вызываешь. или в чем тормоза?

vvviperrr ★★★★★
()
Ответ на: комментарий от vvviperrr

Я опять неправильно написал, ага.

Все остальные функции из списка перестают вызываться на время Sleep.

Deleted
()
Ответ на: комментарий от stevejobs

Затем, что это медленно, костыльно и будет кричать убей меня? Вот серьёзно - заводить тред, который моргает курсором. Ты бы вмёржил такой код в мастер?

pon4ik ★★★★★
()
Ответ на: комментарий от vvviperrr

прерывания таймера конечно есть

Фига, я думал там только кварц и магические числа. Очень далеко от встраиваемых дел я нахожусь :)

pon4ik ★★★★★
()
Ответ на: комментарий от Deleted

Потому что тебе Sleep надо вызывать для потока в котором выполняется функция, написали уже this_thread::sleep_for используй. Внутри функций используй бесконечный цикл. При этом каждая функция должна быть завернута в async, запускай ее с флагом std::launch::async

JoyceGraham
()

Так хотелось?

#include <iostream>
#include <future>
#include <chrono>
#include <string>
#include <mutex>

std::mutex ptmtx;

void foo();
void bar();
void print(const std::string&);

int main()
{
    // future обязательно нужно принять
    auto a = std::async(std::launch::async, foo);
    auto b = std::async(std::launch::async, bar);

    a.wait();
    b.wait();

    std::cout << "Main" << std::endl;

    return 0;
}

void foo()
{
    while(true) {
        std::this_thread::sleep_for(std::chrono::seconds {2});
        print("_Foo");
    }   
}

void bar()
{
    while(true) {
        std::this_thread::sleep_for(std::chrono::seconds {2});
        print("__Bar");
    }   
}

void print(const std::string& msg)
{
    std::lock_guard locker {ptmtx};
    std::cout << msg << std::endl;
}
[vanya@Alyona async]$ g++ -std=c++17 -lpthread main.cpp && ./a.out
_Foo
__Bar
_Foo
__Bar
_Foo
__Bar
^C
[vanya@Alyona async]$

чтобы ещё и кроссплатформенно

Все из стандарта, поэтому, грубо говоря, кроссплатформенно

Rot1
()
Последнее исправление: Rot1 (всего исправлений: 2)
Ответ на: комментарий от Rot1

В этом случае работают не все функции и жрётся процессор на 100%. Ну и ещё printf сильно тормозит. Буду пытаться разбираться, что я делаю не так.

Deleted
()
Ответ на: комментарий от Deleted

В каком смысле тормозит и что не работает? Работу со стандратными потоками нужно (но не обязательно) синхронизировать

PS у меня на GNU/Linux изменений в нагрузке процессора не наблюдается вообще

Rot1
()
Последнее исправление: Rot1 (всего исправлений: 1)
Ответ на: комментарий от Rot1

В каком смысле тормозит

printf очень медленно по букве выводит текст в консоль

что не работает

Работают только три функции из шести

Deleted
()
Ответ на: комментарий от Deleted

Хм. Если в моем коде тело функции print заменить на

std::lock_guard locker {ptmtx};
printf("%s\n", msg.c_str());
fflush(stdout);
то работает без проблем

Rot1
()
Последнее исправление: Rot1 (всего исправлений: 1)
Ответ на: комментарий от Rot1

Ну это короче мои ущербно-специфические проблемы. Буду копать дальше. За пример спасибо.

Deleted
()
Ответ на: комментарий от pon4ik

Он просто есть в любой программе, в самом вырожденном случае там 0 итераций.

Блокирующий системный вызов это уже обработка события - как минимум о готовности устройства к операции ввода/вывода.

Существуют программы без блокирующих системных вызовов.

В остальном согласен. Это я считаю таймер и корутины подвидами потоков, а select/poll разновидностью sleep (точнее sleep частным случаем select). Кстати, насколько я помню, в нормальном цикле обработки событий sleep (хотя бы в виде select) обязателен. Иначе процесс будет вхолостую давать нагрузку на процессор.

monk ★★★★★
()
Ответ на: комментарий от monk

я считаю таймер и корутины подвидами потоков, а select/poll разновидностью sleep

Это уже вопрос модели в голове, если она помогает делать хорошие решения - то почему нет. Лично я не понимаю абстракцию таймера как потока, это либо устройство, либо дополнительное условие на cv. Я сравниваю select/poll с разновидностью cv, которую взводят в ядре. А sleep для меня стоит особняком, т.к. на практике, в загруженной системе sleep практически всегда выполняется ощутимо(на миллисекунды) дольше чем просили(а меньше ему нельзя). Всё не могу себя пнуть посмотреть - есть ли какое то отдельное отношение к безусловному sleep в планировщике по сравнению с ситуацией, когда cv просыпается по таймауту.

Существуют программы без блокирующих системных вызовов.

Тут сложно не согласиться, но они либо не осуществляют ввод/вывод, либо запускаются в качестве операционной системы устройства. Тут стоит сделать оговорку, что я говорю про классические ring3 приложения под управлением ОС с вытесняющей многозадачностью. Ибо в RT системах уже немного другие правила как я могу судить, но опыта работы с хард риалтаймом у меня нет, а в софт риалтайме всё равно уже идёт рассчёт на то когда и где переключится контекст и обычно есть понимание на какой именно код.

pon4ik ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.