Сценарий шаблона связи Haskell Thread

У вас есть два потока: a и b. Поток a находится в непрерывном цикле, прослушивая блокирующий сокет 1. Резьба b также находится в непрерывном цикле, прослушивая блокирующий сокет 2. Оба гнезда 1 и сокет 2 могут возвращать данные в произвольные моменты времени, поэтому Thread a может спать вечно ожидая данных, тогда как поток b постоянно получает данные из сокета и продолжает свою обработку. Это фон.

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

В стандартном императивном языке, таком как python или c, это довольно легко сделать, сделав словарь доступным в обеих областях и только запросив его после того, как поток приобрел блокировку, поэтому Thread B всегда видит наиболее (почти почти) обновленный словарь.

В Haskell я, кажется, изо всех сил пытаюсь придумать хорошую реализацию этого шаблона. MVars, может иметь только один элемент за раз, поэтому не может быть, что Thread a помещается в словарь, поскольку может произойти новое обновление, и он не сможет нажать этот новый словарь, пока Thread b не выберет его из MVar. С другой стороны, если поток b использует MVar для отправки готового сигнала "ok!" для потоковой передачи a это может быть случай, когда Thread a спящий на своем считываемом сокете, поэтому он не сможет отправить обратно данные до тех пор, пока не будет разблокирован его сокет чтения! Есть также каналы, но это кажется беспорядочным, так как мне придется продолжать отправлять новые словари, а Thread B будет отбрасывать все, кроме последнего.

Альтернативное решение, которое будет работать, - просто отправить обновления вниз по каналу и создать поток B для самого словаря. Однако мне интересно, есть ли лучшие альтернативные решения.

Спасибо, что нашли время, чтобы прочитать этот очень длинный вопрос!

Ответ 1

Вы можете использовать MVar следующим образом:

  • Когда поток A получает новые данные, он пытается получить словарь с takeMVar. Когда это удастся, он обновляет словарь и помещает его обратно в MVar
  • Когда поток B получает данные, он пытается получить словарь с takeMVar - в приведенном выше сценарии, где A редко получает данные, которые в среднем будут успешными в среднем. Затем он выполняет поиск и помещает словарь обратно.

Как указывал хаммар, вероятно, лучше не использовать непосредственно takeMVar и putMVar, а скорее обернуть их в modifyMVar_ соответственно. modifyMVar, чтобы не оставить MVar пустым, если один поток получает исключение при использовании словаря.

В потоке A что-то вроде

modifyMVar_ mvar (\dict -> putMVar mvar (insert newStuff dict))

в потоке B вам нужно просто readMVar (спасибо @hammar за то, что указали это).