2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 02:46 
Нужно сделать такую программу. Консольная программа в бесконечном цикле ожидает ввода текстовых команд. Сначала вводим начальные данные, в том числе максимальное время вычислений. Зачем ждем ввода следующей команды. При вводе команды старт нужно запустить вычисления в параллельном потоке и сразу перейти в режим ожидания следующей команды. В процессе вычисления результат постепенно уточняется в зависимости от достигнутой глубины исследования. При истечении определенного времени по таймеру или при вводе команды стоп нужно немедленно остановить вычисления и вывести результат в консоль. Затем все повторяется. Как это лучше организовать?

Пока есть только такая идея.

Всего будет три потока.

Поток0 - главный, который всегда читает команды из ввода.

Когда получаем команду старт, инициируем начальные параметры и запускаем Поток1.

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

Можно как-то сделать так, чтоб Поток1 просыпался не только от сигнала таймера, но и от сигнала стоп из Потока0, и сигнала об окончании вычислений из Потока2?

Какими средствами C++ это можно сделать? Желательно кроссплатформенными, без WinAPI.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 08:11 
Кроссплатформенный - это библиотека pthreads, в том же MinGW присутствует из коробки, или std::thread из C++11, который во многом обёртка из классов над pthreads, так что не сильно суть.
Естественно сперва следует почитать учебник по ней и многопоточному программированию, все эти синхронизирующие примитивы, и т.п., в двух словах не расскажешь.
Собственно после прочтения всё будет и понятно сразу же.
Такое обычно делается так - Поток0 запускает Поток1, выставив нужные синхронизирующие примитивы. Работа Потока1 организована таким образом, что он несколько раз в секунду "отвлекается" от основной работы и проверяет не выставил ли Поток0 ему приказ остановится досрочно, а завершаясь выставляет Потоку0 информацию о том, что завершился и положил результаты куда надо. Поток0, соответственно, выставляет и реагирует.
Очень нехорошо размышлять в терминах "уничтожает" или "досрочно прибивает", потоки должны завершаться добровольно, по своей воле, полностью контролируя происходящее вокруг и в штатном режиме вызывая деструкторы своих объектов.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 13:54 
aa_dav в сообщении #1178273 писал(а):
Кроссплатформенный - это библиотека pthreads, в том же MinGW присутствует из коробки, или std::thread из C++11, который во многом обёртка из классов над pthreads, так что не сильно суть.
Честно говоря, кроссплатформенность не особо нужна, просто не хочется возиться с низкоуровневым winapi. Пока мне удобнее всего использовать встроенный в студию 15 <thread>. Вот только я не нахожу там всех аналогов pthread. Например, pthread_cancel и pthread_testcancel.

aa_dav в сообщении #1178273 писал(а):
Естественно сперва следует почитать учебник по ней и многопоточному программированию, все эти синхронизирующие примитивы, и т.п., в двух словах не расскажешь.
Уже читал и про мьютексы, и про условные переменные. Все равно затрудняюсь сообразить, как все устроить.

Цитата:
Поток0 запускает Поток1, выставив нужные синхронизирующие примитивы. Работа Потока1 организована таким образом, что он несколько раз в секунду "отвлекается" от основной работы и проверяет не выставил ли Поток0 ему приказ остановится досрочно
Наверное, нужно в общей памяти поставить флаг, который Поток1 будем периодически проверять, захватывая мьютекс? На частые обращения не расходуется много ресурсов?

Цитата:
а завершаясь выставляет Потоку0 информацию о том, что завершился и положил результаты куда надо.
Поток0 во время работы Потока1 ждет ввода текстовых команд на cin. Как он может на что-то реагировать?

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 19:28 
Я так понимаю, это делает не Поток0, а Поток2, запущеный тоже из нулевого.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 22:23 
Аватара пользователя
ellipse
ellipse в сообщении #1178320 писал(а):
Наверное, нужно в общей памяти поставить флаг, который Поток1 будем периодически проверять, захватывая мьютекс?

Верно.
ellipse в сообщении #1178320 писал(а):
На частые обращения не расходуется много ресурсов?

Это сложный вопрос, в том смысле что как правило все новички ошибаются или делают поспешные выводы. Причем как в одну сторону так и в другую.
Если там есть паузы, то расходуется мало.
ellipse в сообщении #1178320 писал(а):
Поток0 во время работы Потока1 ждет ввода текстовых команд на cin. Как он может на что-то реагировать?

aa_dav не учёл особенности консольных приложений. Да может. Но давайте считать что нет, так как у консольного приложения нет очереди сообщений. Соответственно две задачи ждать таймер и ждать ввод не могут быть обработаны независимо в одном потоке. Соответственно пока не закончится ввод таймаут не сработает.
В разных потоках обработать можно.

ellipse в сообщении #1178320 писал(а):
Вот только я не нахожу там всех аналогов pthread. Например, pthread_cancel и pthread_testcancel.

http://en.cppreference.com/w/c/thread

cnd_wait - blocks on a condition variable
cnd_timedwait - blocks on a condition variable, with a timeout

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение19.12.2016, 22:58 
А зачем непременно нужен Поток1, почему нельзя в переменную положить время окончания работы Потока2 и в нём самом несколько раз в секунду проверять не превысили ли это время и если да, то корректно останавливаться и закрываться? Всё равно же в нём проверять флаг требования останова, можно их легко совместить в одно проверяемое условие. И сигнал останова из Потока0 в Поток2 посылать как обнуление этого запомненного времени останова - Поток2 быстро и корректно сам по нему остановится.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение20.12.2016, 09:51 
aa_dav в сообщении #1178273 писал(а):
Работа Потока1 организована таким образом, что он несколько раз в секунду "отвлекается" от основной работы и проверяет не выставил ли Поток0 ему приказ остановится досрочно
Поскольку Поток 1 по условию не делает никакой работы, а ждёт срабатывания таймера, то надо просто ждать либо срабатывания таймера, либо нотификации (что само по себе уже сложная задача, см. ниже) из Потока 0.

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

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение20.12.2016, 11:06 
warlock66613 в сообщении #1178540 писал(а):
Поскольку Поток 1 по условию не делает никакой работы, а ждёт срабатывания таймера


Не в предложенной мной схеме. В моей схеме основной работой, включая вычисления и проверку на таймер делает Поток1. Забыл это подчеркнуть. Его рабочие циклы просто сконструированы так, чтобы не менее, чем, к примеру, 10 раз в секунду проверять не прошло ли достаточно времени или не появилось ли флагов завершения от Потока0.
Кстати насчёт консоли - да, она не очень хорошо в это ложится, т.к. ввод/вывод от двух потоков могут пересекаться, что неаккуратно как минимум. Хотя, если для личных нужд, а не коммерции, с этим вполне можно жить.
Я бы сделал по другому - Поток0 выводит подобие меню:
(A)bort e(X)it (S)how ... и так далее
и в цикле спит 100 мс, а после проверяет нет ли результатов от Потока0 либо нет ли в буфере клавиатуры введенных символов.
вполне себе интерактивно получится и без рисков пересечения выводов, потому что выводить будет только Поток0.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение23.12.2016, 03:14 
Аватара пользователя
ellipse в сообщении #1178242 писал(а):
Желательно кроссплатформенными, без WinAPI

Обычно принципиально не считается плохим тоном использовать абстрактрые обертки - например, для Win32 одну, для линукс-освместимых другую, тогда архитектурно и три потока можно будет смело сократить до двух. А ожидание на одном объекте модернизировать не только до ожидания на нескольких, но и вообще уйти в асинхронную модель.
Если задача конечно не из класса принципиально ограниченных в используемом инструментарии.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение23.12.2016, 21:09 
bondkim137 в сообщении #1179353 писал(а):
например, для Win32 одну, для линукс-освместимых другую
В случае современного C++ обёртки уже написаны, остаётся только научиться ими пользоваться.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение24.12.2016, 03:08 
Аватара пользователя
Для уровня лаборатных - да, а так остаются нюансы.
Чем глубже копать, тем больше последних всплывает и портит жизнь.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение01.01.2017, 11:05 
Pavia в сообщении #1178449 писал(а):
http://en.cppreference.com/w/c/thread

Диванного видно сразу.

Идём по ссылке
http://en.cppreference.com/w/c/thread писал(а):
If the macro constant __STDC_NO_THREADS__(C11) is defined by the compiler, the header <threads.h> and all of the names listed here are not provided.

Проверяем
gcc писал(а):
$ gcc -std=c11 -dM -E - < /dev/null | grep __STDC_NO_THREADS__
#define __STDC_NO_THREADS__ 1

Лол.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение01.01.2017, 19:35 
Аватара пользователя
Ну да gcc не является образцовым компилятором. Оттого ему весь стандарт поддерживать не надо.
Но если уж хочется можете скачать https://github.com/jtsiomb/c11threads

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение03.01.2017, 20:51 
ellipse в сообщении #1178242 писал(а):
Можно как-то сделать так, чтоб Поток1 просыпался не только от сигнала таймера, но и от сигнала стоп из Потока0, и сигнала об окончании вычислений из Потока2?


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

Вам нужен manual reset event, который сам вычислительный поток проверяет и корректно завершается если флажок взведен.

 
 
 
 Re: Как запустить параллельный поток и остановить по таймеру?
Сообщение13.01.2017, 12:54 
Pavia в сообщении #1181304 писал(а):
Оттого ему весь стандарт поддерживать не надо.
Стандарт явно говорит, что можно: либо иметь thread.h, либо определить __STDC_NO_THREADS__. Второй вариант такая же полная поддержка стандарта, как и первый. Так что у gcc всё в этом плане в порядке.
Pavia в сообщении #1181304 писал(а):
Но если уж хочется можете скачать https://github.com/jtsiomb/c11threads
Зачем брать куцую обёртку над POSIX-тредами, когда можно взять сами POSIX-треды?

 
 
 [ Сообщений: 15 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group