Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: ammaximus от Ноябрь 02, 2014, 12:52



Название: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 02, 2014, 12:52
По не зависящим от меня причинам необходимо использовать ряд процессов, которые используют общую память. Как правило при этом изменяет содержимое памяти лишь один из них, а остальные являются читателями.

Возможно ли в этом случае использовать механизм, аналогичный QReadWriteLock и как?

Насколько я понял, QSharedMemory::lock()  - это монопольный мутекс на использование памяти, QSystemSemaphore тоже не помогли.

Может запихать объект QReadWriteLock в SharedMemory, но будет ли в этом смысл?


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 02, 2014, 13:40
QReadWriteLock никакого отношения к шаред памяти не имеет. Вам нужно QSharedMemory + QSystemSemaphore. После того как процесс записал данные в общую память он сообщает др процессам что данные готовы (QSystemSemaphore) - ну дальше разберетесь


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 02, 2014, 15:39
Значит я не понял как пользоваться QSystemSemaphore. Можно в двух словах применительно к этой задаче? То есть понятно как сделать с его помощью блокирующий вызов, а как сделать аналог QReadWriteLock. Чтобы пишущий процесс вставал в приоритетную очередь, иначе возможно, что  он вообще никогда не сможет дождаться записи, поскольку читателей очень много.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 02, 2014, 20:12
.., а как сделать аналог QReadWriteLock. Чтобы пишущий процесс вставал в приоритетную очередь, иначе возможно, что  он вообще никогда не сможет дождаться записи, поскольку читателей очень много.
Хмм... уточните как читатели читают если их много? Не получится ли что двое одно и то же прочитали?
В двух словах так: писатель взводит флажок (напр pending_write) в шаред памяти и ждет пока счетчик читателей num_readers (там же в шаред) обнулится. Увидев pending_write != 0 новый читатель не должен пытаться читать и увеличивать  num_readers, 


Название: Re: QSharedMemory QReadWriteLock
Отправлено: vulko от Ноябрь 02, 2014, 20:58
Если читателей много и 1 писатель, используй ReadWriteLock.

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


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 03, 2014, 01:27
Есть один процесс, который иногда обновляет общую память по приходу из канала. Есть несколько процессов, которые периодически лазают в общую память и читают ее содержимое. Их интересует только текущее состояние памяти.

Igors:
pending_write - это QSystemSemaphore? Т.е. к каждому сегменту общей памяти нужно приставить два семафора. (по сути один для учета читателей, а pending_write будет работать через встроенный QSharedMemory.lock()). Это вариант, попробую.

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

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

Поэтому я сейчас пишу свой вектор (шаблон), который получает в параметре максимальный размер (для моей задачи это допустимо) и выделяет разделяемую память T*maxsize. После этого я гарантирую, что не выйду за пределы памяти.

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


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 03, 2014, 10:35
pending_write - это QSystemSemaphore? Т.е. к каждому сегменту общей памяти нужно приставить два семафора. (по сути один для учета читателей, а pending_write будет работать через встроенный QSharedMemory.lock()). Это вариант, попробую.
Там не так просто. Я обдумаю и отпишусь. Можно ли хранить классы в шаред памяти - ну как минимум new/delete не будет нормально работать, и этого достаточно чтобы не искать таких приключений  :)


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 03, 2014, 11:38
Аттач


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 03, 2014, 15:19
Код:
QAtomicInt

Сам бы не догадался) Все действительно непросто, буду разбираться, спасибо.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Авварон от Ноябрь 03, 2014, 16:07
Есть один процесс, который иногда обновляет общую память по приходу из канала. Есть несколько процессов, которые периодически лазают в общую память и читают ее содержимое. Их интересует только текущее состояние памяти.

Igors:
pending_write - это QSystemSemaphore? Т.е. к каждому сегменту общей памяти нужно приставить два семафора. (по сути один для учета читателей, а pending_write будет работать через встроенный QSharedMemory.lock()). Это вариант, попробую.

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

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

Поэтому я сейчас пишу свой вектор (шаблон), который получает в параметре максимальный размер (для моей задачи это допустимо) и выделяет разделяемую память T*maxsize. После этого я гарантирую, что не выйду за пределы памяти.

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

используйте std::vector, у него можно задавать свой аллокатор. Как это делать - в гугл(


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 03, 2014, 16:25
используйте std::vector, у него можно задавать свой аллокатор. Как это делать - в гугл(
Еще один фанат вектора :) Я бы не связывался, если в шаред требуется что-то выше POD структур - сериализовать и все дела.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: vulko от Ноябрь 05, 2014, 09:24
Есть один процесс, который иногда обновляет общую память по приходу из канала. Есть несколько процессов, которые периодически лазают в общую память и читают ее содержимое. Их интересует только текущее состояние памяти.

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

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

Поэтому я сейчас пишу свой вектор (шаблон), который получает в параметре максимальный размер (для моей задачи это допустимо) и выделяет разделяемую память T*maxsize. После этого я гарантирую, что не выйду за пределы памяти.

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

Понятно.
А обязательно должны быть процессы? Переделать на потоки нельзя?


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 05, 2014, 14:31
Да, такое условие. Решается задача повышения надежности - есть процесс-родитель (диспетчер) он следит за состоянием процессов и восстанавливает их в случае вылета. Не знаю, насколько этот подход правильный, но пока менять его не будем.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 06, 2014, 09:57
1. Забыл указать, у меня Qt4.8, там нет QAtomicInt::load(). Насколько это критично?
2. Не совсем понял, что происходит здесь:
Код:
if (dst.testAndSetOrdered(cur, val)) 
return !cnt->mActiveReaders;  // enable write if there are no active readers

Код:
	if (!BeginWrite(data->mCount))
writeSemaphore.acquire();

// write data content
theMem.lock();

Если нет активных читателей мы пропускаем захват семафора, но сразу делаем lock?


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 06, 2014, 11:01
1. Забыл указать, у меня Qt4.8, там нет QAtomicInt::load(). Насколько это критично?
Тогда замените на просто присваивание, напр cur = dst

2. Не совсем понял, что происходит здесь:
Код:
if (dst.testAndSetOrdered(cur, val)) 
return !cnt->mActiveReaders;  // enable write if there are no active readers

Код:
	if (!BeginWrite(data->mCount))
writeSemaphore.acquire();

// write data content
theMem.lock();

Если нет активных читателей мы пропускаем захват семафора, но сразу делаем lock?
В любом случае мы увеличиваем число писателей. Если на этот момент не было активных читателей - писатель сразу идет писать, иначе ждет на семафоре пока последний отчитавшийся его не откроет. В общем случае неск писателей могут прорваться на запись, поэтому по записи надо делать lock/unlock (а вот по чтению нет).


Название: Re: QSharedMemory QReadWriteLock
Отправлено: vulko от Ноябрь 06, 2014, 12:08
Да, такое условие. Решается задача повышения надежности - есть процесс-родитель (диспетчер) он следит за состоянием процессов и восстанавливает их в случае вылета. Не знаю, насколько этот подход правильный, но пока менять его не будем.

интересный подход к надежности...)))


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 06, 2014, 13:16
Цитировать
интересный подход к надежности...)))

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

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


Название: Re: QSharedMemory QReadWriteLock
Отправлено: vulko от Ноябрь 06, 2014, 13:20
Цитировать
интересный подход к надежности...)))

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

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

ну если данных не так много, то можно и сокеты.

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


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 06, 2014, 13:29
Я предлагал многопоточную реализацию, но у нас что-то присосались к этой системе и изменить это не выходит.
Не раз видел обратную ситуацию (хотят процессы вместо потоков). Процесс можно прибить, а вот поток нет


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 06, 2014, 18:13
Есть у процессов некое преимущество, и даже их медлительность при переключении для нас не критична. Меня лишь огорчает необходимость изобретать велосипеды. Сегодня весь день убил на аналог вектора с форычем и еще не факт, что все правильно.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Old от Ноябрь 06, 2014, 18:42
и даже их медлительность при переключении для нас не критична.
Нет у них никакой медлительности.
В linux процессы переключаются с той же скоростью, что и потоки. Точнее потоки в linux, это те-же процессы, которые делят одно адресное пространство.

И подход с процессами правильный, в дальнейшем вы много полезного сможете получить.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: VPS от Ноябрь 06, 2014, 20:11
Сегодня весь день убил на аналог вектора с форычем и еще не факт, что все правильно.
Вроде как библиотека Boost.Interprocess имеет свои реализации контейнеров для использования их в разделяемой памяти.


Название: Re: QSharedMemory QReadWriteLock
Отправлено: ammaximus от Ноябрь 06, 2014, 23:36
Цитировать
И подход с процессами правильный, в дальнейшем вы много полезного сможете получить.
Спасибо, очень хотел услышать подобное))
Цитировать
Вроде как библиотека Boost.Interprocess имеет свои реализации контейнеров для использования их в разделяемой памяти.
Уже выбрана Qt, придется обойтись ее средствами. Но я подгляжу как там у них)


Название: Re: QSharedMemory QReadWriteLock
Отправлено: Igors от Ноябрь 07, 2014, 11:04
Меня лишь огорчает необходимость изобретать велосипеды. Сегодня весь день убил на аналог вектора с форычем и еще не факт, что все правильно.
А нужны ли такие хитрости? Можно держать в шаред простые массивы, напр "счетчик" и дальше следуют элементы. Что-то более сложное - сериализовать. При обращении к шаред прочитать данные (де-сериализовать), сразу же освободить шаред,  и потом, в "своей" памяти, спокойно разбираться. Если массив(ы) трудно зарезервировать по максимуму, то создавать для него новый (отдельный) шаред блок при перезаписи.