863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
1 | |
Синхронизация доступа к разделяемой памяти21.02.2017, 12:08. Показов 10137. Ответов 73
Метки нет (Все метки)
Когда потоки являются дочерними по отношению к процессу тут все просто - объект мьютекса находится в общей памяти и используя этот объект можно делать mutex.lock() определенной секции а при завершении работы mutex.unlock();
А как синхронизировать доступ к данным shared memory между процессами? Подозреваю что в таком случае мьютекс нужно хранить разделяемой памяти Но как конкретно это реализуется плохо представляю Подскажите пожалуйста кто знает
0
|
21.02.2017, 12:08 | |
Ответы с готовыми решениями:
73
Реализация стека строк в разделяемой памяти (MPI) Как сохранить данные контейнера в разделяемой памяти Есть ли оверхед от использования разделяемой памяти, в сравнении с глобальной? Аська на основе разделяемой памяти |
nmcf
|
21.02.2017, 15:59
Синхронизация доступа к разделяемой памяти
#21
|
Не по теме: А разве с помощью new можно выделить память, которая будет доступна другим процессам? Или это особенность Linux?
0
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
21.02.2017, 16:25 [ТС] | 23 |
Точно. Согласен
Копипаст криво сделал... Насколько я понимаю весь отрезок шаред памяти будет захвачен одним процессом пока он его не освободит, так? Добавлено через 26 секунд Да, именно модель
0
|
21.02.2017, 16:28 | 24 |
Чисто на всякий случай. Процесс захватывает не отрезок памяти, а мьютекс. А уже в голове программиста (т.е. в логике работы программы) захваченный мьютекс означает запрет на использование указанного отрезка памяти
1
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
||||||
21.02.2017, 16:46 [ТС] | 25 | |||||
Я думал туда отрезок памяти надо передать (в данном случае указатель на шаред память).
но при таком вызове
как передать нужный адрес
0
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
21.02.2017, 17:23 [ТС] | 27 |
Evg,
Тогда не понимаю, как применить этот пример для того что бы получить то, что мне нужно. Насколько я понял он подходит только для тех случаев когда у одного процесса есть много потоков Мне же нужен некоторый атомарно изменяемый процессами флаг на который я могу ориентироваться при чтении/записи из шаред мемори. Пока думаю подходит вариант описанный тут: Синхронизация доступа к разделяемой памяти Если более опытные видят что я чего-то не понимаю, прошу меня поправить.
0
|
2780 / 1933 / 570
Регистрация: 05.06.2014
Сообщений: 5,598
|
|
21.02.2017, 19:32 | 28 |
Если вы про мой пример, он подходит для всего что видит atomic_flag. Вам вроде бы никто не мешает положить его в шаред-мемори по фиксированному смещению (иначе придется думать как сообщить процессам по какому смещению флаг искать).
1
|
21.02.2017, 20:02 | 29 |
Насколько я вижу, ты почти всё не понимаешь. Просто я не могу понять, что ты в конечном итоге хочешь. Что конкретно ты хочешь добиться, когда "нужен некоторый атомарно изменяемый процессами флаг на который я могу ориентироваться при чтении/записи из шаред мемори"
"Нормальная" работа с разделяемым ресурсом выглядит, например, так. Есть некий ресурс, который обладает свойсвтом, что одновременно с ним разрешено работать только одному потоку/процессу. Это может быть файл, это может быть массив, это может быть сетевое соединение, принципиальной разницы нет. В каждому такому ресурсу пристраивается некий семафор, который в простом случае состоит из двух положений: занято и свободно (именно к этому семафору нужно прикладывать атомарные операции и прочие велосипеды). Поток хочет использовать файл, перед тем, как его использовать, он смотрит на семафор. Если семафор в положении "занято", значит поток либо спит, либо занимается своими делами, либо ковыряет в носу, ожидая освобождения семафора. Как только семафор переключился в положение "свободно", поток нажимает на кнопку "попробуй занять семафор" и получает в ответ результат "ты занял семафор" либо "ты не занял семафор, т.к. какой-то параллельный поток нажал кнопку раньше тебя". В обоих случаях семафор встал в положение "занято". Во втором случае (не успел занять) поток опять ковыряет в носу. В первом случае (успел занять) поток приступает к работе с файлом, делает то, что нужно, после чего нажимает на кнопку "освободить семафор". Семафор переходит в состояние "свободно", а поток продолжает заниматься своими локальными делами Это один из простейших вариантов работы. В каждом конкретном случае такая модель может выглядеть по разному. Семафоров может быть несколько. Они могут быть более, чем с двумя состояниями. Т.е. твоя задача состоит в том, чтобы запрограммировать семафор, описать интерфейсы, читающие состояние ("занято" или "свободно"), "попробуй занять семафор", "освободить семафор", может что-то ещё нужно. Этот объект-семафор должен находиться в разделяемой памяти, т.е. быть доступным для всех процессов
1
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
21.02.2017, 21:28 [ТС] | 30 |
Кажется я понял. Каждый процесс будет крутить цикл пока состояние флага не будет "свободным" и его нельзя будет занять. А когда займет - пойдет дальше и выполнит все что ему нужно, а потом изменит состояние флага в исходное.
Вот я опасаюсь следующего: Если предположим мы обрабатываем сетевое соединение в процессе и какой то процесс очень невезучий и все никак не может захватить доступ к критической секции. Выходит что клиент может ждать условно говоря бесконечно. Чего делать? Таймауты лепить? Или есть другие решения? И еще меня беспокоит следующее: насколько я знаю менеджеры задач ОС не любят задачи которые работают в "холостую" и могут понижать им приоритет выполнения при смене с них контекста (не уверен, но где-то об этом читал). Вот спинлок как раз работает много в холостую. Так же сталкивался с мнением что работающие в холостую потоки "засыпают" т.е перестают что-то делать. В таком случае интересно если поток процесса уснет то кто будет проверять освободилась ли критическая секция? И еще один вопрос: Представим что некий вызов пытается захватить мьютекс по вашему примеру. Но мне не важно он захватит его или нет, то есть эту операцию можно будет попробовать выполнить в следующий раз если в этом возникнет необходимость. В таком случае как я понимаю цикл не нужен? Можно просто в функции посмотреть если !atomic_flag_test_and_set_explicit значит прерываем работу функции и так в следующий раз когда появится необходимость в данном вызове. Такое решение конкретно для данной ситуации не считается ошибкой? Evg, Вот мне нужно все то что ты написал
0
|
2780 / 1933 / 570
Регистрация: 05.06.2014
Сообщений: 5,598
|
|
21.02.2017, 21:39 | 31 |
Да, таймаут - стандартное решение.
Не засыпают. А вот создать тормоза по всей системе вполне могут. Поэтому, спинлок - решение для коротких захватов ресурса. Да, можно и так.
1
|
21.02.2017, 21:50 | 32 |
Каким конкретно способом поток будет ковырять в носу, зависит от конкретной задачи
Если строишь велосипеды, то наверняка такое может случиться. Если использовать готовые решения, то скорее всего этих проблем не будет, т.к. эти проблемы авторы уже учли Всё зависит от того, что такое "вхолостую" Спинлок - это, грубо говоря, специальный вид мьютекса, для которого процесс ковыряния в носу настроен на ожидание в цикле освобождения семафора. Мьютексы нужно использовать там, где критическая секция короткая. В таких условиях будет намного эффективнее проболтаться десяток тактов в цикле, чем тратить тысячи тактов на засыпание и просыпание Так реализован мьютекс в glibc. Т.е. для мьютекса ковыряние в носу настроено на засыпание. Мьютексы нужно использовать там, где ожидание может быть сравнительно долгим или потоков много и они гарантированно будут ломиться к ресурсу одновременно в большом количестве. В таких случаях засыпание приведёт к снижению нагрузки на процессор по сравнению с ожиданием в цикле Операционная система его разбудит, когда освободится мьютекс При этом вариант ковыряния в носу (жужжать или спать) - это заранее запрограммированное действие, а вовсе не результат деятельности планировщика задач Да По сути так и надо делать, если я правильно понимаю, что делает atomic_flag_test_and_set_explicit. Такая операция по умному называется trylock
1
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
21.02.2017, 22:06 [ТС] | 33 |
Думаю добавление новые данные в контейнер можно назвать коротким захватом.
Вот еще что интересно: при чтении данных из контейнера тоже нужно мьютекс захватывать? Я думаю что нужно потому что если в читаемое значение пишет другой процесс например int, то может возникнуть ситуация что пишущий поток успеет положить туда например только 2 байта и прочитаем мы не понятно что. Ок если при чтении тоже нужно захватывать блокировку то что делать в следующей ситуации: Предположим что запись в контейнер происходит один раз для каждого ключа, здесь для чтения ставить блокировку не вижу смысла. Вот думаю не писать int а сделать pair, где first будет флагом bool и равен true после успешной записи а second значением. А при чтении уже проверять если флаг тру - значит значение есть и его можно читать. Но тут снова проблема - выходит из за одной записи при каждом чтении нужно будет проверять данный флаг что не эффективно. Если флаг один раз был тру - то больше его проверять смысла нет, но при таком раскладе придется. Можете что нибудь посоветовать по этому поводу? Добавлено через 10 минут Т.е в холостую считается когда вообще ничего не делается? В случае с примером Renji проверяется переменная. Можно пример такой холостой работы? А вот спят 5 потоков. Кого первым разбудит ОС? Того кто первый приперся за локом?
0
|
2780 / 1933 / 570
Регистрация: 05.06.2014
Сообщений: 5,598
|
|
21.02.2017, 22:18 | 34 |
Запись меняет метаинформаию (сколько ключей, где они, etc). Чтение использует метаинформацию чтоб найти данные. Итого, не будет блокировки - два процесса столкнутся лбами при обращении к метаинформации.
Это не проблема, так как основную часть времени процесс работает с локальными данными, для которых спинлоки не нужны. А задержки синхронизации возникающие раз в миллион тактов не критичны. Если же процессы постоянно лезут в глобальные данные в шаред мемори, значит у вас ошибка проектирования многопоточного приложения.
0
|
21.02.2017, 22:29 | 35 |
Если данные короткого размера, то да
Нужно. Хотя вот здесь как-раз и пригодится конструкция, состоящая больше, чем из 2 состояний. Т.е. здесь было бы полезно иметь 3 состояния: свободно, захвачено для чтения, захвачено для записи. При таком раскладе захват для чтения означает возможность читать нескольким потокам одновременно и запрет кому бы то ни было писать. Но здесь, разумеется, потребуется иметь счётчик для количества читателей У тебя нет никакой гарантии, что если флаг равен true, то значение уже записано. Нет, ты конечно можешь сначала писать значение, а потом переводить флаг в состояние true, но тебе нужно уметь сделать это так, чтобы эти записи в память гарантированно отработали в таком порядке. Т.е. делать их специальным образом, вероятно, семантика Relaxed нужна именно для таких целей В общем виде задача не смысла не имеет. Т.е. нужна конкретная постановка задачи. За счёт алгоритма можно тут что-то сделать (типа того, что есть ситуации, в которых мы гарантированно знаем, что запись уже была, а потому экономим на флаге) Я имел в виду, что конкретно ты называешь словом "вхолостую" Использование ptread_mutex_lock: http://pubs.opengroup.org/onli... _lock.html Скорее всего внутри реализации используется поддержка ОС. Либо какая-то самопальная реализация очереди, чтобы гарантированно исключить ситуации, когда ты долбишься в семафор и никак не захватишь Зависит от реализации. Логично было бы, чтобы была очередь желающих. Соответственно будить их надо в том порядке, в каком они встали в очередь Добавлено через 1 минуту Для ptread_mutex_lock завтра попробую посмотреть, что в glibc'ях сделано
1
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|||||||||||
21.02.2017, 22:47 [ТС] | 36 | ||||||||||
Понятно, спасибо
Интересная мысль. Спасибо Ну например по контексту видно что ничего не происходит. Например попытка прочитать в цикле залоченный файл Вот именно так и думаю сделать. Первым писать флаг. А бывает разве так что сначала запишется второе значение потом первое? Если да то можно сделать что-то вроде
Добавлено через 7 минут Ступил. Сначала данные - потом флаг. Но мой пример
0
|
21.02.2017, 23:02 | 37 |
Забей, я об одном, а ты о другом
Первым его точно нельзя писать. Потому как параллельный поток прочтёт, что флаг взведён и полезет читать данные. А данные могут ещё не быть записаны. Две записи может переставить компилятор. Их может переставить аппаратура. Процессор в момент исполнения потока операций явно не видит, что две операции записи обязаны быть исполненными именно в такой последовательности, поэтому там, где есть out of order execution, он по каким-то соображениям может их поменять местами. Их может поменять местами контроллер памяти (по тем же соображениям) Я не программирую на плюсах и мало что знаю о классе std::atomic, но relaxed запись условно выглядит как C++ atd::atomic a, b; a.store (1, std::memory_order_relaxed); b.store (2, std::memory_order_relaxed); Добавлено через 4 минуты Пардон, про relaxed я наврал. Тут какая-то другая семантика должна быть. Т.е. та, которая нам обеспечит правильный порядок операций записи в память Добавлено через 3 минуты Возможно, должно быть так. std::atomic'ом должен быть только флаг. Запись значения "готово" в флаг должна выполняться с семантикой release (это обеспечит исполнение тех записей в память, что стоят выше по коду). Чтение (проверка) флага должно быть с семантикой acquire. Но тут меня надо перепроверять Добавлено через 4 минуты Т.е. что-то типа того: C++ struct Data { std::atomic<bool> flag; int data; }; struct Data *Element; // Запись элемента в одном потоке Element->data = val; Element->flag.store (true, memory_order_release); // Чтение элемента в другом потоке if (Element->flag.load (memory_order_acquire) == true) val = Element->data;
0
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
21.02.2017, 23:15 [ТС] | 38 |
Evg,
Поищу инфу про эти семантики, спасибо! Не по теме:
0
|
22.02.2017, 09:19 | 39 |
http://en.cppreference.com/w/c... mory_order
Добавлено через 10 минут https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
1
|
863 / 451 / 89
Регистрация: 10.06.2014
Сообщений: 2,643
|
|
22.02.2017, 11:37 [ТС] | 40 |
Evg,
Спасибо за ссылки, почитаю. Я подумал насчет перестановок про которые ты говорил. Думаю компилятор может переставить последовательность выполнения в целях оптимизации, если он думает, что это не нарушит общую логику выполнения. Иначе последовательный код был бы всегда не надежным и программы "то работали бы как задумано, то нет". Учитывая это я думаю все таки можно сначала писать данные с локом, но на чтении не лочить доступ а просто смотреть флаг, нет?
0
|
22.02.2017, 11:37 | |
22.02.2017, 11:37 | |
Помогаю со студенческими работами здесь
40
Запись и считывание разделяемой памяти Хранение указателей в разделяемой памяти Считать структуру из разделяемой памяти Сделать массив из 10 int в разделяемой памяти Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |