Как ленивое вычисление взаимодействует с MVars? - PullRequest
6 голосов
/ 07 августа 2020

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

Один из способов реализовать это - используйте mvar :: MVar () и обеспечьте взаимное исключение следующим образом:

thread = do
   ...
   _ <- takeMVar mvar
   x <- readFile "somefile"  -- critical section
   putMVar mvar ()
   ...
   -- do something that evaluates x. 

Вышеупомянутое должно нормально работать на строгих языках, но если я чего-то не упускаю, у меня могут возникнуть проблемы с этим подходом в Haskell . В частности, поскольку x оценивается только после поток выходит из критического раздела, мне кажется, что файл будет прочитан только после поток выполнил putMVar, что, в первую очередь, лишает смысла использование MVars, так как несколько потоков могут читать файл одновременно.

Проблема, которую я описываю, реальная, и если да, то как мне ее обойти ?

Ответы [ 2 ]

9 голосов
/ 07 августа 2020

Да, это реально. Вы можете обойти это, избегая всех функций base, которые реализованы с использованием unsafeInterleaveIO. У меня нет полного списка, но это как минимум readFile, getContents, hGetContents. Действия ввода-вывода, которые не выполняют ленивый ввод-вывод, такие как hGet или hGetLine, подходят.

Если вам необходимо использовать ленивый ввод-вывод, полностью оцените его результаты в действии ввода-вывода внутри критической секции. , например, путем объединения rnf и evaluate.

Некоторые другие комментарии по связанным вещам, но они не являются прямым ответом на этот вопрос:

  • Лень и ленивый ввод-вывод - это действительно отдельные понятия. У них случается одно имя, потому что люди ленивы давать имена. Большинство операций ввода-вывода не связаны с ленивым вводом-выводом и не сталкиваются с этой проблемой.

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

1 голос
/ 12 августа 2020

readFile следует называть unsafeReadFile, потому что это небезопасно так же, как unsafeInterleaveIO. Если вы избегаете функций, которые имеют или должны иметь префикс unsafe, тогда у вас не будет этой проблемы.

Haskell - это не язык с ленивой оценкой. Это язык, в котором, как и в математике, порядок оценки не имеет значения (за исключением того, что вы не должны тратить неограниченное количество времени, пытаясь оценить аргумент функции до оценки тела функции). Компиляторы могут изменять порядок вычислений по соображениям эффективности, и GH C делает это, поэтому программы, скомпилированные с GH C, как правило, не оцениваются лениво.

readFile (вместе с getContents и hGetContents) - одна из небольшого числа стандартных функций Haskell без префикса unsafe, которые нарушают семантику значений Haskell. GH C должен специально отключать свои оптимизации, когда сталкивается с такими функциями, потому что они делают программные преобразования наблюдаемыми, которые не должны быть наблюдаемыми.

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

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