LINUX.ORG.RU

Синхронизация потоков

 


0

1

Привет всем. Встал такой вопрос.

Как из одного потока заставить что-то сделать другой поток? Речь идет о программировании с агломерацией библиотек GTK. Причем вешать нежелательно ни один из потоков. По незнанию сделал через idle-функцию, которая время от времени мониторит состояние переменной, в зависимости от состояния делает нужное действие или не делает. Но может быть есть способ лучше?

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

Писать на питоне - не предлагать!

★★★★★

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

Почитай Стивенса (IPC в UNIX), там описано много разных способов.

Простейший — сигналы (но там много чего не передашь), если нужно много информации передавать — можно воспользоваться разделяемой памятью, сокетами или еще чем.

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

Наверное, мне бы нужен хороший мануал по созданию своих сигналов.

Сигналы в смысле не сигналы Unix, а сигналы в терминах агломерации GTK.

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

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

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

А не проще абстрагироваться от всяких GTK и использовать обычные юниксовые способы межпотокового взаимодействия?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Ushenin

Idle-функция большую часть времени крутится просто так, ничего не делая, просто проверила состояние переменной и дальше спать.

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

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

ТС: все известные мне сишные API работы с потоками позволяют потоку вернуть void*, вот в него и нужно положить нужные данные. Например, pthread_create (вызванный в основном потоке) создаёт дополнительный поток, а pthread_join (вызванный в основном потоке) ждёт его завершения - и принимает void*, возвращённый им. В дополнительном потоке нужные данные передаются как void* в pthread_exit. Есть также pthread_tryjoin_np, чтобы проверить, завершился ли дополнительный поток, не дожидаясь его завершения в противном случае.

В gtk будет нечто аналогичное. Впрочем, pthreads - часть POSIX (за исключением ЕМНИП pthread_tryjoin_np), и от смешения их с gtk плохого не будет.

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

у потоков память уже расшаренная

Точно. Туплю.

Необходимо лишь механизм блокировок не забывать.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Ushenin

Не вариант. Оно заблокирует поток. Низя.

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

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

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

GAsyncQueue вроде должно, хотя я плохо понял что именно надо. Сигналы вообще не то — это способ ставить хуки на методы объекта.

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

Да, это клево. Я свой велосипед написал, похожий на эту очередь. Спасибо большое, теперь буду знать, что такое уже есть!

Сейчас вопрос стоит так - Как из одного потока заставить выполнить действие другой поток?

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

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

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

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

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

Сейчас вопрос стоит так - Как из одного потока заставить выполнить действие другой поток?

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

Основной поток приложения мониторит события gtk и спит в остальное время, следовательно, гарантированно вмешаться в его работу можно только отправив событие в gtk. Обработчик сигнала - это, по сути, ещё один поток, да ещё и урезанный, т.к. во время его работы состояние всех остальных потоков заморожено на какой угодно стадии, и даже puts/printf вызвать нельзя; можно лишь атомарно установить какую-нибудь переменную, а её потом всё равно придётся читать.

quiet_readonly ★★★★
()

man 2 pipe, man 2 signalfd, man 2 timerfd_create, man eventfd, man 7 unix, man poll, man select, man epoll

тысячи их.

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

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

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

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

anonymous
()

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

UFO-man
()
Ответ на: комментарий от UFO-man

Возможно, конечно, это я не могу отойти от некой модели и мыслить шире, но по мне, так если например интерфейс (GUI) создается в одном потоке, то и писать в виджеты должен только он. Другие потоки могут готовить данные, но работать с виджетами лучше одному потоку.

Сильно закостенелая мысль?

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

Не сильно, тк в интро к гтк сказано, что работать с виджетами можно только из главного потока, или с помощью глобальной блокировки, подробнее: http://developer.gnome.org/gdk3/stable/gdk3-Threads.html

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

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

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

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

Там кстати описано, какая возникает при этом возня, и почему в 3.6 мультитрединг стал deprecated.

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

Сильно закостенелая мысль?

Сильно хорошая мысль, только так и можно достичь отзывчивого интерфейса. Как-то чувствуется разница между QtCreator, который парсит код на фоне и реагирует без задержек, и плагином save for web для гимпа, который после каждого изменения любого параметра пересчитывает всё изображение (1-2 сек), блокируя интерфейс.

Что касается создания GUI из нескольких потоков, то тут нужно хорошее абстрагирование от метода рисования, вроде OpenGL. Вот в OGL один поток может, скажем, поворачивать свою кость 3D модели, другой уже передавать пересчитанные вершины своей кости.

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

Единственно правильный вариант. Никогда, ни при каких условиях, с интерфейсом не должны работать более одного потока одновременно. И блокировки тут не помогут.

note173 ★★★★★
()

Отвечаю для случая с Gtkmm (C++-обертка для Gtk+). Последовательность действий такова: создать thread-safe (!!!) очередь сообщений (собственно команд), в которую посылающие команды потоки добавляют их (команды) на последующее исполнение в GUI-потоке (можно использовать паттерн проектирования Команда). После добавления команд(-ы) в очередь поток должен как-то уведомить GUI-поток о том, что поступили новые команды на исполнение. Вот в последнем действии и кроется загвоздка, ибо использовать для этого сигналы в данном случае нельзя, т.к. libsigc++ (эта библиотека используется в Gtk+ для сигналов-слотов) не является thread-safe. Для этого в Gtkmm имеется класс Glib::Dispatcher, метод Glib::Dispatcher::emit() которого может быть вызван из любого потока инициируя, тем самым, вызов всех подключенных к экземпляру класса Glib::Dispatcher методом Glib::Dispatcher::connect() слотов в GUI-потоке. Таким образом, пусть подключенный к экземпляру класса Glib::Dispatcher слот «разгребает» вышеуказанную очередь и выполняет полученные от других потоков команды. При этом нужда в глобальной блокировке всего GUI отпадает.

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