LINUX.ORG.RU

Posix threads: rwlock

 posix threads


0

3

Задача: нужно создать кучу потоков, которые читают разделяемые данные. И есть один, который иногда (очень редко) меняет эти данные. Напрашивающееся решение: использовать rwlock. Это точно лучше, чем mutex. Но есть засада: при попытке получить rwlock на чтение может быть возвращена ошибка: достигнут предел на количество читателей.

Вопрос: как мне узнать заранее этот предел? Если я буду его знать, то у меня будут варианты: либо молча уменьшить макс. кол-во потоков до этой величины, либо сообщить юзеру, что я не могу обеспечить заданное им кол-во потоков, и завершиться после этого. Так как мне узнать (или задать) эту величину? Google даёт только ссылки либо на тот же man, который у меня и так есть, либо на пересказ этого же man’а своими словами с опечатками.

У меня программы должны работать десятилетиями, я не могу методом тыка определять такие величины: создавать потоки, лочить rwlock, затем при ошибке выдать сообщение о предельном кол-ве потоков, которое вбить гвоздями в программу. Сегодня это одна величина, а через 20 лет на другой версии GNU/linux она будет другой. Есть ли тут аналог sysconf(3)? Вот я могу запросить в run-time sysconf(_SC_OPEN_MAX). И исходя из этого не наступить на проблему, когда мне нужно держать открытыми много файлов одновременно.

А в случае POSIX threads вместо предсказуемого поведения программы мне предлагаются грабли. На которые я могу наступить в неизвестный мне заранее момент времени. Кто вообще писал такую спецификацию? Спек должен быть максимально конкретным, а не вот это вот расплывчатое, невнятное говно. Туда же до кучи: где мне получить величину PTHREAD_STACK_MIN? В man’е pthread_attr_setstacksize(3) она указана как 16384 байта (linux-specific). Я должен вбить гвоздями в программу константу 16384? Где, чёрт возьми, она за-define-нена? В pthread.h её нет. Ни sysconf(3), ни getrlimit(2) не дают её мне. Кто мне гарантирует, что через 10 лет она не изменится, и моя программа не перестанет работать? А где мне взять константу PTHREAD_THREADS_MAX, на которую ссылается man pthread_create(3p)?

Чем больше думаю на эту тему, тем меньше хочется использовать POSIX threads, а больше хочется заюзать напрямую clone(2). И получить предсказуемое поведение. И гори он огнём, этот POSIX с его невнятной спецификацией! Программа, написанная лет 20-25 назад с использованием linux-специфичного packet socket до сих пор работает и есть не просит.

Но всё-таки хочется же переносимости – по возможности. Может кто знает, где нарыть документацию на POSIX threads? Более внятную, чем тупо перепечатка man’ов на функции. И конкретно на rwlock: как получить (указать?) максимальное количество потоков-читателей? Opengroup не предлагать, там те же man’ы.

★★

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

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

А не потому что разраб сломал себе мозг, возможно усложнил обслуживание, зато каким-то чудом компилятор выдаёт 20 ошибок вместо 30. POSIX, на мой взгляд, здесь не помощник: грош цена стандарту, которым никто не умеет пользоваться.

kaldeon ★★
()

Вопрос: как мне узнать заранее этот предел?

Ответ: на практике тебе не надо знать этот предел, потому что если ты создал тредов больше, чем он, ты уже настолько крупно облажался, что ничего поделать нельзя, и в лучшем случае программа давно упала, а в худшем - всё стоит колом.

r--r--r--
()

А как насчёт варианта отказаться от блокировок вообще?

Храним атомарный указатель на структуру данных (если компилятор старый без atomic, то просто volatile). Писатель при записи создаёт новую структуру в куче и атомарно перезаписывает указатель без блокировок. Соответственно, читатель тоже не пользуется блокировками, а просто берёт указатель и читает данные по нему.

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

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

Это хорошая идея, но не годится. Объём данных таков, что памяти может запросто не хватить. Может и хватит, но риск получить ENOMEM очень большой. Не хотелось бы вылета программы с такой ошибкой.

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

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

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

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

А для освобождения старой памяти после того как все читатели с ней закончли - подойдёт механизм RCU (активно используется в ядре, но есть и подходы его применения в userspace)

GPFault ★★★
()

Спецификация там такая, потому что pthreads кроссплатформенное.

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

где мне получить величину PTHREAD_STACK_MIN

А где мне взять константу PTHREAD_THREADS_MAX

Всмысле «где взять»? Просто аользуешься этими константами в своей проге если они тебе нужны.

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

Задача: нужно создать кучу потоков, которые читают разделяемые данные. И есть один, который иногда (очень редко) меняет эти данные. Напрашивающееся решение: использовать rwlock. Это точно лучше, чем mutex.

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

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

Спецификация там такая, потому что pthreads кроссплатформенное.

Да это-то понятно. Я знаю, как POSIX расшифровывается.

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

Насчёт исходников – это всё понятно, но возникает закономерный вопрос: а нахрена мне тогда POSIX threads, если я могу просто использовать linux-специфичный clone(2) напрямую? POSIX же придумано для переносимости, а значит не должно требовать чтения исходников (иначе в чём смысл-то?). Насчёт «думаю оно скорее всего 32-битное»: мне неинтересны предположения по этому поводу, я и сам могу предположить, что там int, а не char. Но мне нужно гарантировать Заказчику работоспособность. Разница между «предполагаю» и «гарантирую» понятна же, да?

Всмысле «где взять»? Просто аользуешься этими константами в своей проге если они тебе нужны.

Каким образом «пользуешься»? Они не определены нигде. Ну или скажи, что мне включить, помимо pthread.h, чтобы получить их. В pthread.h ни одна из них не определена. Ты можешь проверить это самостоятельно: включи pthread.h, stdio.h и попробуй вывести эти константы через printf. Увидишь сообщения об ошибках gcc: символы неопределены.

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

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

Я понимаю, что ты тут самый умный. Так будь добр, просвети меня, как правильно решать задачи синхронизации?

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

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

Для того чтобы гарантировать в любом случае надо изучать целевую систему, да.

Они не определены нигде.

Я обычно в таких случаях grep -RF по /usr/include использую, тут тоже помогло:

$ grep -RF PTHREAD_STACK_MIN /usr/include
/usr/include/pthread.h:   minimal size of the block must be PTHREAD_STACK_MIN.  */
/usr/include/pthread.h:   to be started.  This size must never be less than PTHREAD_STACK_MIN
/usr/include/x86_64-linux-gnu/bits/local_lim.h:#define PTHREAD_STACK_MIN	16384
/usr/include/bits/local_lim.h:#define PTHREAD_STACK_MIN	16384
/usr/include/i386-linux-gnu/bits/local_lim.h:#define PTHREAD_STACK_MIN	16384
$ grep -RF PTHREAD_THREADS_MAX /usr/include
/usr/include/x86_64-linux-gnu/bits/local_lim.h:#undef PTHREAD_THREADS_MAX
/usr/include/bits/local_lim.h:#undef PTHREAD_THREADS_MAX
/usr/include/i386-linux-gnu/bits/local_lim.h:#undef PTHREAD_THREADS_MAX
$ grep -RF local_lim.h /usr/include
/usr/include/x86_64-linux-gnu/bits/posix1_lim.h:#include <bits/local_lim.h>
/usr/include/bits/posix1_lim.h:#include <bits/local_lim.h>
/usr/include/i386-linux-gnu/bits/posix1_lim.h:#include <bits/local_lim.h>
$ grep -RF posix1_lim.h /usr/include
/usr/include/x86_64-linux-gnu/bits/posix1_lim.h:#endif	/* bits/posix1_lim.h  */
/usr/include/limits.h:# include <bits/posix1_lim.h>
/usr/include/dirent.h:#  include <bits/posix1_lim.h>
/usr/include/bits/posix1_lim.h:#endif	/* bits/posix1_lim.h  */
/usr/include/i386-linux-gnu/bits/posix1_lim.h:#endif	/* bits/posix1_lim.h  */
(это devuan 4 = debian 11, amd64)

PTHREAD_THREADS_MAX походу нигде нет и правда, PTHREAD_STACK_MIN - в limits.h

В FreeBSD (13.x amd64) оба в pthread.h и кстати stack_min там 2048 всего, а threads_max ulongmax

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

Да, спасибо за совет. Не заметил этот комментарий раньше, он в тему. Насчёт аккуратности – тут действительно это нужно. Я же и не сказал, что прям сразу начну реализовывать идею, которую предложил KivApple. Это хорошая идея, но нужно подумать о всяких деталях. Читатель теоретически может быть вытеснен надолго, а значит писатель не может сразу освобождать память после изменения указателя. Но сама идея – хороша. Разбить здоровенный блок памяти на кучу мелких. Ну т.е. не структура с буферами, а структура с указателями на буфера. Это чуть сложнее, но решает вопрос из задачи – без блокировок структуры как целого. С каждым из буферов можно работать по отдельности и не нарваться на ENOMEM.

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

bits/local_lim.h кстати вот что содержит:

/* The number of threads per process.  */
#define _POSIX_THREAD_THREADS_MAX       64
/* We have no predefined limit on the number of threads.  */
#undef PTHREAD_THREADS_MAX
Видимо 64 это какой-то устаревший лимит, везде написано что в линуксе явного лимита на треды одного процесса нет (но есть лимит на общее количество из нескольких sysctl и rlimit).

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

Есть! Действительно, PTHREAD_STACK_MIN определён в limits.h. Это уже хорошо. Но хотелось бы, чтобы хоть где-то в документации на POSIX threads это было упомянуто (ну хоть в каком-то man’е). Ведь будут же ещё треды на LOR’е по этому поводу, я ж не один такой, кому это нужно. Где, блин, исчерпывающая документация для программиста? Почему он должен искать ответы на форуме, а не в описании стандарта? Нахрена такой стандарт?

Насчёт PTHREAD_THREADS_MAX. Если мы не имеем в виду всякую экзотику, то понятно, что с т.з. ядра это процессы. Но оно же должно быть доступно программе, работающей в user-space, причём доступно стандартизированным способом. Так чего-ж его нету-та на уровне POSIX threads?

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

Дык, я ж не против стандартизации, я наоборот за. Но стандарт должен быть конкретным, а не размазанным, как говно по тарелке. В этом и причина создания данной темы: нет ни одного стандартного варианта для программиста, который бы гарантировал корректность работы его программы. Ну чего бы стоило добавить платформо-специфичную функцию, которая возвращала бы min stk size, max threads per process, max rwlock readers? Ничего это не стоило, но не сделали. Почему, блин?

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

«Документация по POSIX threads» это сам POSIX-стандарт. Можешь им предложение прислать чтобы добавили туда это в следующей редакции (если она будет).

Насчёт ограничения конкретно в линуксе - в нём нет per-process ограничения, зато можно уткнуться в sysctl pid_max, sysctl threads_max, rlimit nproc, а так же всякие cgroups лимиты, и половину этих величин (если не все) можно менять в реалтайме, ну а так же какая-нить форк-бомба может сожрать лимит процессов и твоей проге ничего не останется. Какой толк от того что прога узнает разово это число, если оно постоянно меняется? Относись к этому как в количеству свободного места на диске, количеству доступной оперативной памяти, лимиту на количество открытых файлов - какое-то есть, но предсказанию не подлежит и зависит от кучи факторов, как неизбежных физических, так и всяких квот.

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

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

Я тебе, конечно, очень благодарен за то, что ты нашёл мне PTHREAD_STACK_MIN, но всё-таки: «лимиту на количество открытых файлов». Я ж уже приводил этот пример в заглавном топике: sysconf(_SC_OPEN_MAX). Я знаю в run-time, сколько мне можно открыть файлов. И могу исходя из этого принимать решения в программе: закрыть какой-то из файлов, если достигнут этот лимит, чтобы открыть новый. Программа в итоге будет работать чуть медленнее (теоретически, на практике я с этим не сталкивался), но не вылетит с ошибкой! Вот этого я и хочу от системы – предсказуемости, потому что этого требует от меня Заказчик.

«Документация по POSIX threads» это сам POSIX-стандарт. Можешь им предложение прислать чтобы добавили туда это в следующей редакции (если она будет).

Там ведь где-то потерялась табличка «Сарказм», да?

Насчёт ограничения конкретно в линуксе - в нём нет per-process ограничения

Ок, допускаю, что я чего-то не знаю про linux. Вот скажи мне: если я буду создавать pipe до бесконечности – я упрусь в ограничение? Или это будет не per-process, а per-user?

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

не точно :-) без cond_timedwait вся вкуснота пропадает. Лучше сгородить «структурку-диспетчер» и самому считать читателей/писателей.

А поясни, пожалуйста, свою мысль. А то она какая-то не очень понятная. Зачем городить «структурку-диспетчер» вместо rwlock?

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

sysconf(_SC_OPEN_MAX)

А оно учитывает sysctl fs.nr_open и fs.file-max?

Там ведь где-то потерялась табличка «Сарказм», да?

Да нет, если ты действительно хочешь чтобы это туда внесли, то это единственный способ, по-моему. Насчёт его успешности я не уверен.

создавать pipe

pipe или тред?

Если тред то per-user ограничение есть - getrlimit(RLIMIT_NPROC). Не знаю чему оно равно, но может оказаться что в глобальное sysctl kernel.threads-max быстрее упрёшься (я так понял что его дефолт зависит от количества физической памяти). В других системах может быть по-другому.

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

Так будь добр, просвети меня, как правильно решать задачи синхронизации?

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

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

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

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

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

А оно учитывает sysctl fs.nr_open и fs.file-max?

Ну я не знаю. Вообще-то, согласно стандарту, это моё ограничение. Т.е. per-process, а не вообще per-user. Но опять же, как я уже писал, на практике я с этим не сталкивался.

Да нет, если ты действительно хочешь чтобы это туда внесли, то это единственный способ, по-моему. Насчёт его успешности я не уверен.

Ну понятно, табличка «Сарказм» у тебя там всё-таки есть.

pipe или тред?

Вот тут именно про pipe, поскольку он давний, всем известен и понятен. Есть ведь ограничение на pipe-per-process. Или оно в linux per-user? Вопрос навеян тем, что ты утверждал, что я в программе не могу ориентироваться ни на какие лимиты. Что странно для меня и непривычно. Противоречит это моему опыту работы под UNIX. Очень хочу услышать от тебя пояснений про linux-специфичность.

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

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

При таком способе обмена данными все потоки работают на полную мощность, загружая все ядра процессора

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

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

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

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

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

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

Нет, чувак, поток просто будет ждать данных от хранителя, а не «заниматься своими делами». Потому что ему (потоку) нужны эти данные – для обработки. Ему тупо нечем заниматься другим. Он данные обрабатывать должен. А иначе нафига он (поток) нужен вообще?

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

Не слышал про pipe-per-process. Учитывая что pipe это тот же файл, к ним как минимум лимиты на открытые дескрипторы применяются (_SC_OPEN_MAX?). Может быть ещё какие-то лимиты на выделение ipc буферов есть (точно не знаю), но это тоже другое.

Только я не понимаю при чём тут pipe? Речь шла про треды изначально. Это разные сущности, ограничиваются тоже по-разному.

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

Я такого не писал. Я писал что, если строго подходить к вопросу, то лимит процессов может настраиваться извне и закончиться неожиданно по внешним причинам. А если не строго, то в большинстве случаев ты с этим лимитом не столкнёшься. Особенно учитывая, что делать количество тредов сильно больше чем количество процовых ядер + дисков смысла мало.

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

Это ты типа семафор в «службу» завернул и сразу сильно умным стал? Или у тебя «служба» внутри с помощью духа святого Хоара работает?

Если я правильно понял, то служба у него – это отдельный процесс. Который ведёт БД. Но в однопоточном режиме. Поэтому синхронизация для него неактуальна. А то что все, кто хочет с этой БД работать, должны стоять в очереди (прощай, параллелизация вычислений), то это проблемы негров, которые шерифу неинтересны.

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

Это ты типа семафор в «службу» завернул и сразу сильно умным стал? Или у тебя «служба» внутри с помощью духа святого Хоара работает?

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

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

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

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

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

Не слышал про pipe-per-process.

Не смог сходу найти, но это точно есть. Сам наткнулся на эту проблему где-то в конце 90-х. Из-за чего пришлось программу перепроектировать (Tcl/Tk front-end кучу Си-шных back-end’ов для обработки запускал и вешал каждого на отдельный pipe, в итоге словил ограничение на кол-во pipe). Что там было – изначальная SunOS (клон BSD) или Solaris (SVR4) – я уже не помню.

Только я не понимаю при чём тут pipe? Речь шла про треды изначально.

Это да. Но мне стало интересно, чего я не знаю про linux, а ты знаешь. Ты ж приводил всякие заклинания «sysctl pid_max, sysctl threads_max», в чём я не секу. Вот и стало интересно. «Какой толк от того что прога узнает разово это число, если оно постоянно меняется?». Т.е. для проги лимиты узнавать бесполезно? Неожиданное для меня открытие, вот и интересуюсь подробностями. А потом:

Я такого не писал. Я писал что, если строго подходить к вопросу, то лимит процессов может настраиваться извне и закончиться неожиданно по внешним причинам. А если не строго, то в большинстве случаев ты с этим лимитом не столкнёшься. Особенно учитывая, что делать количество тредов сильно больше чем количество процовых ядер + дисков смысла мало.

Про проц и диск понятно. Но про лимиты в linux вообще непонятно. Так они есть? Они per-process или per-user? Или их вообще нет, и заморочиваться мне, как программисту, незачем?

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

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

Дык, если поток создаётся только для обработки данных, то какую ещё кучу действий он может совершать? Ему тупо нечего делать, пока он не дождётся данных из хранилища. Т.е. ты таким подходом фактически убиваешь параллелизацию вычислений. Не, ну это тоже можно, не вопрос. Вопрос: нахрена ж так делать-то?

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

Лимиты есть. Бывают и per-process, и per-user, и per-ещё-что-то. Полного их списка я не знаю. Конкретно лимита «threads per process» в линуксе нет. В других ОС может быть. Вроде бы единственные относительно кроссплатформенные лимиты это те что в man getrlimit, остальное все по-своему делают.

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

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

А какими способами отец русской демократии производит синхронизацию потоков?

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

Конкретно лимита «threads per process» в линуксе нет. В других ОС может быть.

Что-ж, спасибо и на этом. Но тут ведь опять встаёт вопрос: а зачем мне прослойка POSIX threads, если я должен заложиться на linux-специфичности? Если стандарт, претендующий на переносимость, не даёт мне бонусов переносимости, то зачем мне такой стандарт? Можно же с тем же успехом заюзать linux-специфичную функцию clone(2). И избежать всех геморроев POSIX threads, как то использование ими (или не использование – заранее неизвестно) 2 RT-сигналов. Непонятность, что будет с SIGCHLD – это будет обрабатывать библиотека? Вообще-то в linux по умолчанию странная ситуация с SIGCHLD. Если я позову sigaction на SIGCHLD, то кто будет обрабатывать завершение потока? Вобщем, куча непонятного геморроя, и непонятно, нафига он мне нужен, когда есть простой и понятный clone(2)?

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

А какими способами отец русской демократии производит синхронизацию потоков?

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

Enthusiast ★★★★
()

Прочитал, задумался: а не проще тогда thread pool сделать. Условных 32 потока (ну или сколько там хочется, хоть по числу ядер выбирать), которые будут задачи хватать из очереди по принципу «первый освободившийся». Казалось-бы, особых минусов не наблюдается. Или задача такая, что нужно рожать адовое число потоков (что, конечно, не есть хорошо, но если требуется)?

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

Зачем городить «структурку-диспетчер» вместо rwlock?

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

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

зачем мне прослойка POSIX threads, если я должен заложиться на linux-специфичности?

специфичность linux в том что его время от времени внезапно ломают ;-)

Ты твёрдо уверен что линус,молнар со товарищи не депрекейтнут clone() в ближайшие 5 лет ?

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

специфичность linux в том что его время от времени внезапно ломают

Когда последний раз из ядра целенаправленно удалили системные вызовы или поменяли их аргументы на несовместимые с предыдущим вариантом?

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

Когда последний раз из ядра целенаправленно удалили системные вызовы или поменяли их аргументы на несовместимые с предыдущим вариантом?

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

делать что-то исключительно ОС-специфичное, да ещё Linux, только за тройную оплату.

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

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

Ты придумал mutex.

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

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

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

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

Когда последний раз из ядра целенаправленно удалили системные вызовы или поменяли их аргументы на несовместимые с предыдущим вариантом?

я ещё застал чехорду с rt-таймерами.

Но ты не ответил на вопрос. Давай я повторю его ещё раз: когда в ядре (подсказка - не форках и левых бранчах) меняли API для user space несовместимым образом последний раз?

r--r--r--
()

Казалось бы, ищется без проблем:

$ grep -R PTHREAD_STACK_MIN /usr/include/ | grep define
/usr/include/x86_64-linux-gnu/bits/pthread_stack_min-dynamic.h:#   define PTHREAD_STACK_MIN __sysconf (__SC_THREAD_STACK_MIN_VALUE)
/usr/include/x86_64-linux-gnu/bits/pthread_stack_min.h:#define PTHREAD_STACK_MIN	16384
$ grep -R pthread_stack_min-dynamic.h /usr/include/
/usr/include/pthread.h:# include <bits/pthread_stack_min-dynamic.h>
ttnl ★★★★★
()
Ответ на: комментарий от r--r--r--

Но ты не ответил на вопрос. Давай я повторю его ещё раз: когда в ядре

я тебе вполне ответил - ломают постоянно и всё. То что происходит я ведре доверия не вызывает.

подрастёшь, обращайся

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

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

Это звучит как перенос проблемы в другое место кода

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

Ты второй раз отвечаешь на пост, но не отвечаешь на вопрос.

ломают постоянно и всё.

И по этому тебе совершенно нечего предъявить по факту?

То что происходит я ведре доверия не вызывает.

Доверия пока что не вызывают твои посты.

обращайся

Но зачем обращаться к тому, кто не вызывает доверия?

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

отправляет запрос на чтение в службу-хранитель

Что означает эта фраза? Там по-любому будет какая-то очередь для этих запросов. А значит будет многопоточная структура данных для реализации этой очереди. И ты просто заменил простой многопоточный примитив сложным. Можно, а зачем? Тебя актёры покусали?

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