Два процесса, один производитель/один потребитель в Windows. Что лучше мьютекс, событие или семафор

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

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


person cloudraven    schedule 21.11.2012    source источник


Ответы (1)


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

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

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

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

Пока у вас есть только один производитель и один потребитель, и вы рассматриваете все как один "фрагмент" данных, который контролируется вместе, этого достаточно. Однако это может привести к проблеме. Давайте рассмотрим, например, интерфейсную часть веб-сервера в качестве производителя и внутреннюю часть в качестве потребителя (и некоторый отдельный механизм для передачи результатов обратно на веб-сервер). Если буфер достаточно мал, чтобы вместить только один запрос, производителю, возможно, придется буферизовать несколько входящих запросов, пока потребитель обрабатывает один. Каждый раз, когда потребитель готов обработать запрос, производитель должен остановить свои действия, скопировать запрос в буфер и сообщить потребителю, что он может продолжить.

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

Итог: выбор не в скорости. Речь идет о том, как вы используете/структурируете данные и доступ процессов к ним. Предполагая, что это действительно так просто, как вы описываете, пара событий, вероятно, является самым простым механизмом, который будет работать.

person Jerry Coffin    schedule 21.11.2012
comment
Спасибо за ваш подробный ответ. Я определенно забочусь сначала о правильности, а затем о производительности. Узкое место не в области разделяемой памяти. Один процесс получает команду от пользователя и следит за тем, чтобы другой процесс получил ту же команду. Это происходит примерно 100 раз в секунду. На самом деле необходимо, чтобы производитель останавливался до тех пор, пока потребитель не потребляет данные. Для аналогичной проблемы, но в многопоточном режиме, мне нравится использовать объекты Criticalsection/Conditionvariable. Кажется, согласно тому, что вы сказали, я могу использовать объекты Event аналогичным образом. Это правильно? Спасибо! - person cloudraven; 22.11.2012
comment
@cloudraven: Судя по тому, о чем вы говорите, события должны работать. OTOH, если объем задействованных данных невелик, поэтому вам в основном нужна минимальная задержка, может быть намного проще обрабатывать его через канал (который в значительной степени представляет собой небольшой буфер разделяемой памяти, но управляется внутри ядра, чтобы свести к минимуму трудности и накладные расходы). - person Jerry Coffin; 22.11.2012
comment
но, конечно... использование именованных каналов имеет гораздо больше смысла в этом конкретном случае. Совсем про них забыл. Спасибо - person cloudraven; 23.11.2012