Scorp__)
Молодой специалист
Offline
Пол:
|
|
« : 03-02-2007 16:29 » |
|
Вот такое дело Вопрос не срочный, просто, скорее тема для дискуссии и сбора информации. Как лучше обеспечивать межобъектное взаимодействие? В первую очередь интересует обмен информацией. К примеру есть фильм. Один объект разбирает его на кадры, другой как-то эти кадры обрабатывает, третий собирает фильм с обработкой. Как лучше поступить, копировать информацию в каждый объект? Организовать какое-то общее хранилище? На сегодняшний день я использую копирование. При этом, организован объект "очередь", который может накапливать информацию, до определенного предела. При этом два обменивающихся друг с другом объекта имеет интерфейс к этой общей очереди. Но может быть можно сделать обмен быстрее и с меньшим количеством занимаемой памяти? Ну и раз уж речь про взаимодействие, то как можно обойтись без специфичной для ОС синхронизации? Если каждый объект работает в своем потоке, то поневоле приходится их как-то синхронизировать, а WinAPI-то не объектное, и код получается некрасивый. Мне кажется, что многим будет интересно услышать как это делается "правильно" Или узнать какие приемы в каких случаях применяются.
|
|
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #1 : 03-02-2007 16:39 » |
|
Scorp__), через сокет можно попробовать , я думаю )
|
|
|
Записан
|
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #2 : 03-02-2007 16:41 » |
|
И если они в одном процессе? Просто в разных потоках?
Я же говорю, тут скорее тема для дискуссии, а не поиск конкретной реализации, конкретная реализация у меня имеется и я скорее доволен ей чем нет. Так как при больших объемах информации, которые через эту очередь гоняются, она не теряет данные, и не загружает процессор. Только копирование больших объемов меня смущает, но пока не более. По-крайней мере при видеозахвате полного экрана 1024*768*32 50 кадров в секунду, ничего не тормозило ))
Можно считать, что я надеялся сохранить эту тему для следующих поколений. Чтобы все желающие могли узнать побольше об этом кирпичике программы.
|
|
« Последнее редактирование: 03-02-2007 16:47 от Scorp__) »
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #3 : 03-02-2007 16:47 » |
|
Scorp__), а, тогда ещё проще )
но в одном процессе проще пользовать критические секции - некий третий класс обмена, через него должны "проходить" данные обоих обменивающихся объектов. Критическая секция обеспечит, что потоки данных не "пересекутся"
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #4 : 03-02-2007 18:44 » |
|
Алексей, витеевато ты что-то говоришь. Критические секции - один из методлов синхронизации в многопоточных приложениях, основанный в выделении критического участка кода. (Это так, от себя, не сказать бы что определении верное). ПРо некий третий класс для обмена. Посмотри, Scorp__) не о том же писал? Смысл-то тот же
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #5 : 03-02-2007 21:35 » |
|
Scorp__), если это потоки одного процесса, то зачем копировать большие объемы информации, если проще выделить каждую порцию в отдельную структуру и передавать просто указатели, а синхронизацию выполнить на мутексах или подобных механизмах? Если не обязательно в одном процессе, то тоже самое можно реализовать на разделяемой памяти. При этом вместо указателей нужно передавать смещение от начала блока - каждый процесс может иметь свой адрес начала блока.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #6 : 03-02-2007 22:32 » |
|
RXL, а если один из объектов выдает очень быстро информацию? А другой медленнее ее обрабатывает, тогда нужно накопление. При этом нужна полная параллельность работы. Как при видеозахвате, нету времени ждать пока там что-то обработается, кадр надо сохранить, а мьютекс не дает. Еще раз повторю, хотелось бы подробнее увидеть разные подходы обсудить их достоинства и недостатки. И не в ракурсе конкретной реализации с помощью существующих механизмов, а скорее в терминах ООП. Чтобы получше разобраться в собственных мыслях, так сказать Итак, мой подход дает удобное управление информацией, при этом взаимодействующим объектам не надо ничего знать кроме интерфейса к очереди. Один кладет данные и ни о чем не думает, другой забирает. Очередь осуществляет буферизирование. Еще у меня сделано так, что можно подписаться на событие от очереди, о том, что в нее поступил новый кадр, после того как она была пуста. Сделано это для того, чтобы не гонять опрос пустой очереди попусту (ой, каламбур ). Управление памятью централизуется в одном объекте, синхронизация не нужна и возможно полностью параллельное выполнение потоков, ни один не будет ждать другого, за исключением ситуации, когда кому-нибудь просто нечего делать. Недостатки: необходимо копирование информации, возможно, достаточно больших объемов. Трудно организовать взаимодействие одного ко многим, когда каждому из многих нужен один и тот же кадр информации. К примеру, в случае нескольких разных анализаторов, просматривающих один поток информации. Придется делать к каждому свою очередь - очень много памяти и копирования. О передаче указателей я задумывался, действительно соблазн большой, уж очень быстро получается. Но возникает вопрос, как пометить ненужную уже информацию? В моем случае есть два списка: с информацией и свободных буферов. Когда в очередь кладут информацию, она копируется в один из свободных буферов и он помечается занятым и ставится в список занятых. Когда информацию забирают, аналогичное действие происходит с занятыми буферами. Если свободных не хватает, то память довыделяется, но не освобождается до конца работы. А как подобное провернуть с передачей указателей, я не придумал. Ведь по сути, мы позволяем чужим объектам творить с памятью в очереди, что угодно. При копировании - все безопасно, пусть он со своей копией, что хочет, то и делает. А с общей памятью... Я освобожу ее, или там будет уже мусор. А чужой объект заныкает полученный указатель в укромном месте и давай к памяти обращаться в самый неподходящий момент. Вот так. В конце концов у нас же клуб, неужели не интересно обсудить проблему целиком? По-моему, из этого обсуждения даже статейка может получиться небольшая.
|
|
« Последнее редактирование: 03-02-2007 22:45 от Scorp__) »
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #7 : 04-02-2007 07:05 » |
|
nikedeforest, я и предлагаю один участок кода защитить ) CCriticalSection critsect;
//процедура-менеджер f1(int mode, void* params) { critsect.Lock();//на "бесконечное" время //молимся, чтобы ничего не зависло :) switch(mode) { case mode_OBJ1_WRITES: { } break;
case mode_OBJ2_WRITES: { } break;
...... ...... ...... }
critsect.Unlock(); }
Scorp__), ты бы привёл конкретный пример, чем занимаешься, а то абстрактно много чего можно напридумывать не того )
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #8 : 04-02-2007 09:37 » |
|
Фвктически описана модель конвейера. Отсюда проистекают решения, давно уже разработанные для промышленных предприятий и физических конвейеров . 1) Асинхронность работы в том смысле, что для некоторых рабочих участков конвейера недопустимо ожидание. В этом случае, в самом деле, используется складирование на входах и/или на выходах. Если участок должен работать непрерывно и равномерно, и в то же время существует риск прерывания или неравномерности входного потока, заводится "сглаживающий" буфер - склад/очередь. При этом размер и заполненность буфера определяют устойчивость системы к сильным колебаниям на входе. Примеров масса - из электроники, из финансового менеджмента, в конце концов, плееры для потокового аудио/видео, передаваемого в компьютерных сетях. Однако размер буфера на входе определяет задержку начала обработки - время заполнения буфера. Поэтому для входного буфера нужно искать компромиссное решение между устойчивостью системы и задержкой при её запуске. Аналогично на выходе, но на выходе нет задержки, поэтому не нужен поиск вышеупомянутого компромисса, и размер буфера можно делать очень большим. В описанном случае, если нет заточки софта под конкретное железо с известной производительностью и неизменным в будущем набором участков конвейера, ничего лучшего, кроме динамического выделения памяти под буферы, не придумать, если желательно экономить память. Если же софт заточен под конкретное железо, и расширение функционала не предполагается, можно в ходе тестирования собрать статистику по расходу памяти и задать статические буферы. 2) Копирование, когда склад используется для распределения потока из одного источника в несколько приёмников или, наоборот, сбор потоков из нескольких источников в общий приёмник. Здесь, в отличие от производства физических предметов, в программировании есть особенность, а именно: копирование - операция гораздо более дешёвая, чем собственно производство. Тем не менее, копирование требует затрат памяти на хранение копий, и возникает вопрос, как эти затраты памяти минимизировать. Минимизировать затраты памяти можно, перемещая точку копирования в те места, где происходит собственно разделение данных. Например, если на входе имеются кадры, которые должны поступать в несколько независимых анализаторов, дешевле по памяти хранить общий входной буфер кадров, нежели заводить отдельный буфер для каждого анализатора. Поскольку кадры до входа в анализаторы остаются неизменными, нет смысла их копировать. Однако сама очередь кадров будет организована несколько сложнее, чем просто последовательность. У очереди должен быть некий контроллер, который обеспечивает постановку в очередь новых кадров из источника и передачу кадров в приёмники. Тем, кто проходил процедуру увольнения или отчисления из какого-либо более-менее крупного учреждения/предприятия, должно быть известно понятие "обходного листа" - такого документа, в котором каждое подразделение ставит отметку о том, что выполнило все процедуры, связанные с уходом, и к уходящему сотруднику/студенту претензий не имеет. Аналогично организуется прохождение медкомиссий, обследований и т.п. При поступлении кадра в очередь контроллер очереди приписывает кадру "обходной лист", в котором перечислены все приёмники. Для каждого приёмника ставится пометка "не пройдено". Фактически на очередь физически кадров (больших по размеру объектов) заводятся несколько очередей (по одной на приёмник) указателей, в которых разделяется общий обходной лист (малый по размеру объект), ссылающийся на кадр; в каждой очереди указатели на обходные листы упорядочиваются в порядке их поступления в приёмник. Контроллер очереди по запросу очередного приёмника выполняет копирование кадра для приёмника и ставит в обходном листе отметку для приёмника "пройдено" (удаляя указатель из очереди приёмника). Как только по всем строкам обходного листа проставлена отметка "пройдено", контроллер очереди удаляет кадр из очереди. Таким образом, копирование собственно кадра выполняется только в самый последний момент, когда уже точно необходимо отделить исходный (неизменный) кадр от обрабатываемого (изменяемого) кадра. Критические секции размещаются в контроллере очереди на операции: добавления кадра и формирование обходного листа, пометки обслуживания приёмника и проверки полного окончания обработки кадра. На прочие операции (копирование, удаление кадра и обходного листа) по здравому размышлению критических секций не нужно, поскольку копирования в разные приёмники друг другу мешать не будут (операция чтения неизменного разделяемого объекта), а процесс удаления выполняется только тогда, когда к кадру больше уже никто не обращается. Возможна блокировка анализаторов (постановка их в ожидание) на операции запроса из очереди кадра, когда кадров в очереди нет, хотя здесь нужно подумать о способе завершения работы всей системы. Такая вот архитектура.
|
|
« Последнее редактирование: 04-02-2007 09:43 от dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #9 : 04-02-2007 10:41 » |
|
dimka, мне вот интересней всего вторая часть.
Дело в том, что анализаторы предполагаются независимыми друг от друга и с совершенно разной производительностью. Пока один будет обрабатывать кадр, другой может обработать пять или десять. И вот разделение в этом случае я не могу придумать. Если нельзя удалять кадр из очереди пока его не скопировали все, то получается что система тормозится до самого медленного, ведь никто не может получить следующий кадр, пока этот не удален. Единственный выход, который я вижу - это усложнить саму структуру очереди, сделав регистрацию приемников, и, выделяя каждому из них собственный указатель на текущий кадр. То есть спроецировать все очереди приемников на одну общую. В принципе, увеличивается только сложность управления, но экономится память.
А в случае синхронного взаимодействия объектов, как я понимаю, лучше организовать разделяемую память. А как это делается в объектной парадигме?
|
|
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #10 : 04-02-2007 11:58 » |
|
Если нельзя удалять кадр из очереди пока его не скопировали все, то получается что система тормозится до самого медленного, ведь никто не может получить следующий кадр, пока этот не удален. Отчего такой вывод? Я такого вывода не делал и даже не подразумевал его. Каждый получает те кадры, которые ему нужны. Если анализатор A обработал кадр 1, он переходит к кадру 2, несмотря на то, что анализатор B ещё не закончил обработку кадра 1. Они же друг другу не мешают. Управлением очередью занимается контроллер очереди, а не анализаторы. После того, как какой-нибудь анализатор уведомит контроллер об окончании обработки кадра (а ещё лучше, если это сделает сам контроллер после копирования кадра для анализатора), контроллер просматривает "обходной лист", и если все пункты отмечены, удаляет кадр, иначе оставляет кадр в очереди для других анализаторов, но он совершенно не обязан тормозить анализатор A. Зачем? Если кадры обрабатывать поштучно, то нет никакого смысла каждому кадру в очереди приписывать "обходной лист". Достаточен лишь 1 "обходной лист" для текущего кадра. Вот если бы я такое сказал, твой вывод был бы понятен... А в случае синхронного взаимодействия объектов, как я понимаю, лучше организовать разделяемую память. А как это делается в объектной парадигме? Не понял вопроса... Причём тут объектная парадигма? К ООП всё вышеизложенное не имеет непосредственного отношения. С другой стороны, непонятно, что ты подразумеваешь под "(не)разделяемой" памятью. Нити (threads) процесса и так выполняются в общем адресном пространстве - они и так все разделяют общую память...
|
|
« Последнее редактирование: 04-02-2007 12:00 от dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #11 : 04-02-2007 12:15 » |
|
dimka, да, вывод сделал я сам, просто я нахожусь во власти своей реализации и сразу понял, что придется кое-что добавить к ней, чтобы все работало правильно. А по второму вопросу, я имел в виду ситуацию без копирования с синхронизацией доступа. Как в этом случае построить объект контролирующий общую область памяти, через которую идет обмен?
|
|
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #12 : 04-02-2007 15:03 » |
|
А по второму вопросу, я имел в виду ситуацию без копирования с синхронизацией доступа. Как в этом случае построить объект контролирующий общую область памяти, через которую идет обмен? Вот здесь не всё известно. Анализатор лишь читает кадры и на основе этого формирует какой-то выходной поток данных? Или же анализатор преобразовывает сами кадры? В первом случае, в самом деле, копировать кадры не нужно. Во втором случае не могут анализаторы одновременно изменять один кадр - это может породить ошибки в их работе. Классический случай писателей и читателей. Каждый писатель требует монопольной блокировки ресурса (от читателей и других писателей). Каждый читатель требует лишь частичной блокировки ресурса (от писателей, но не от других читателей). Всякая обработка кадра должна проходить через две общие для всех анализаторов процедуры: чтения и записи. Каждая процедура как минимум должна содержать в себе код синхронизации. Выполнив и проконтролировав блокировки нитей, каждая процедура должна передать управление процедурам, соответствующим анализаторам. В понятиях ООП: кадр - объект, должен себя блокировать соответствующим образом при обработке запросов к себе со стороны читателей и писателей. Чтобы не блокировать параллельную работу анализаторов, как раз можно копировать кадр для каждого анализатора. Если же анализаторы должны обработать кадр последовательно (друг за другом) - результат работы одного должен поступить на вход другого - параллельность обработки в этом случае организовать нельзя по логическим причинам (если не делить кадры на части).
|
|
« Последнее редактирование: 04-02-2007 15:06 от dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #13 : 04-02-2007 15:38 » |
|
Тоже хочу внести свои две копейки.
Была такая задача - принимать и обрабатывать данные от внешнего источника без потерь. Управлять скоростью входных данных было не возможно. Я сделал два потока - принимающий и обрабатывающий.
Логически эту цепочку передачи из одного потока в другой можно представить в виде двух очередей. В первую помещает данные первый поток, из второй забирает второй поток, а между очередями перекидывает третий. При перегрузке второй очереди третий поток сохраняет данные из первой очереди в файл на диске, а при освобождении считывает из того же файла и помещает во вторую очередь. С одной стороны, в нормальном режиме работы расход памяти был ровно под задачу, а при превышении лимита - все на диск. Блокировку на очередь я ставил на очень короткое время, чтобы только добавить или удалить элемент.
Правда, я упостил реализацию, отказавшись от второй очереди и совместил работу второго и третьего потоков в одном. За год с сервером, на котором работа программа, случались не раз катаклизмы типа перегрузки, но данных программа не теряла.
Кадр ведь должен быть обработан разными потоками в строгой последовательности - будет странно если иначе. Так что принцип "обходного листа" здесь сомнителен.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #14 : 04-02-2007 17:21 » |
|
dimka, вот недостаток опыта сказывается, даже и не пришло в голову подумать, что сам кадр должен быть объектом RXL, в моем случае как раз кадр в идеале должен обрабатываться параллельно разными анализаторами. Каждый ищет что-то свое, так что "обходной лист" тут в тему, только потребуется усложнение самой структуры очереди. Вопрос же про синхронный доступ я задал исключительно из любопытства и для самообразования Узнать как люди это делают. В моем случае, кстати, очередь выполняется в одном потоке. Синхронизация не требуется, поскольку список заполненных буферов построен по типу FIFO и считывание кадра не влияет на добавление кадра в конец. Добавляет и отдает кадры по запросам. Поскольку я не доверяю даже тем чужим классам, которые я сам и буду разрабатывать, никому не позволяю лезть в память объекта, кроме него самого. Сам себя боюсь, лучше уж запретить, от греха подальше Так собственно и получилось копирование кадров
|
|
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #15 : 04-02-2007 20:31 » |
|
Синхронизация не требуется, поскольку список заполненных буферов построен по типу FIFO и считывание кадра не влияет на добавление кадра в конец. Ты рассматриваешь общий случай и игнорируешь крайние случаи - так не пойдёт. Крайний случай - это случай пустой очереди, в которую попадает один кадр. Этот кадр будет разделяемым как для записи (в очередь), так и для чтения (из очереди). Поэтому так вот сразу заявлять ненужность синхронизации я бы не стал - могут появиться трудновоспроизводимые "плавающие" баги. Отсутствие или наличие синхронизации в той или иной точке программы ещё нужно обосновать.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Scorp__)
Молодой специалист
Offline
Пол:
|
|
« Ответ #16 : 04-02-2007 20:45 » |
|
dimka, неа. В случае пустой очереди контроллер очереди сообщит анализатору, когда появился кадр. Ну а если уж тот не слушает сообщений, то есть флажок пустой очереди. И этот флажок сбрасывается, только после полной записи кадра. Хотя на самом деле это и есть синхронизация, только простенькая, как раз для крайнего случая В общем, я опрометчиво заявил, что ее нет
|
|
|
Записан
|
- А Вы сами-то верите в привидения? - Конечно, нет, - ответил лектор и медленно растаял в воздухе.
|
|
|
|