Я пытаюсь понять Corouts, но не совсем понимаю их цель, учитывая существование потоков с forkIO. Какие варианты использования требуют использования сопрограмм по потокам?forkIO и сопрограммы в Haskell
ответ
Это немного непонятно из вашего вопроса, если вы говорите о реализации конкретной версии Haskell coroutines (если да, пожалуйста, добавьте ссылку) или об общей концепции.
Использование forkIO
и некоторая межпоточная связь - это один из способов реализации сопрограмм. Преимущество заключается в том, что таким образом вы можете воспользоваться преимуществами, имеющих несколько процессоров/ядер, но, на мой взгляд, есть несколько недостатков:
- Явный параллелизм
IO
основе, поэтому все ваши расчеты должны выполняться вIO
монады. - Вы должны явно реализовать межпоточную связь.
- Вы должны позаботиться о начале нитей и, что еще важнее, об их утилизации и о предотвращении голода/тупиков.
- Архитектура (очевидно) многопоточная. В некоторых случаях это может быть недостатком. Например, вам может потребоваться, чтобы ваши вычисления были чистыми, детерминированными, однопоточными, но по-прежнему используют концепцию сопрограмм.
Я также предполагаю, что ваш вопрос был о реализации this Coroutine
.
Позвольте мне привести небольшой пример. Предположим, мы хотим вычислить большие факториалы, но так как вычисления могут занять слишком много времени, мы хотим, чтобы он приостанавливался после каждого цикла, чтобы мы могли дать некоторую обратную связь пользователю. Кроме того, мы хотим, чтобы сигнализировать, сколько циклов осталось быть вычислен:
import Control.Monad
import Control.Monad.Coroutine
import Control.Monad.Coroutine.SuspensionFunctors
import Control.Parallel
import Data.Functor.Identity
-- A helper function, a monadic version of 'pogoStick':
-- | Runs a suspendable 'Coroutine' to its completion.
pogoStickM :: Monad m => (s (Coroutine s m x) -> m (Coroutine s m x))
-> Coroutine s m x -> m x
pogoStickM spring c = resume c >>= either (pogoStickM spring <=< spring) return
factorial1 :: (Monad m) => Integer -> Coroutine (Yield Integer) m Integer
factorial1 = loop 1
where
loop r 0 = return r
loop r n = do
let r' = r * n
r' `par` yield n
(r' `pseq` loop r') (n - 1)
run1 :: IO()
run1 = pogoStickM (\(Yield i c) -> print i >> return c) (factorial1 20) >>= print
Теперь предположим, что мы понимаем, что приносит после каждого цикла слишком неэффективно. Вместо этого мы хотим, чтобы вызывающий абонент определил, сколько циклов мы должны вычислить, прежде чем снова приостанавливать.Для достижения этой цели, мы просто заменить Yield
функтор с Request
:
factorial2 :: (Monad m) => Integer
-> Coroutine (Request Integer Integer) m Integer
factorial2 n = loop 1 n n
where
loop r t 0 = return r
loop r t n | t >= n = r' `par` request n >>= rec
| otherwise = rec t
where
rec t' = (r' `pseq` loop r') t' (n - 1)
r' = r * n
run2 :: IO()
run2 = pogoStickM (\(Request i c) -> print i >> return (c (i - 5)))
(factorial2 30)
>>= print
В то время как наши run...
примеры IO
основы, вычисление факториалов являются чистым, они allowe любой монады (включая Identity
).
По-прежнему, используя Haskell's parallelism, мы проводили чистые вычисления параллельно с кодом отчетности (перед тем как приступить к сопрограмме, мы создаем искру, которая вычисляет шаг умножения, используя par
).
И, возможно, самое главное, типы гарантируют, что сопрограммы не могут плохо себя вести. Невозможно, как сопроцессоры могут затормозить - получение или запрос обратной связи всегда сочетается с соответствующим ответом (если вызывающий не решает не продолжать сопрограмму, и в этом случае она автоматически удаляется сборщиком мусора, нет заблокированной нити) ,
Никаких прецедентов не требуется. Все, что вы можете сделать с сопрограммами, вы можете сделать с помощью forkIO
+ канала связи. На самом деле, я считаю, что Go (язык, на котором параллелизм очень дешев, например, в Haskell) вообще избегает сопрограмм и делает все с помощью параллельных легких потоков («goroutines»).
Однако иногда forkIO
является overkill. Иногда вам не нужен , необходимо только разложить проблему на концептуально отдельные потоки команд, которые уступают друг другу в определенных явно определенных точках.
Рассмотрите задачу чтения из файла и записи в другой. Вместо того, чтобы иметь монолитный вложенный цикл, более многоразовым решением было бы составить сопроводительную копию файла с записью файла. Когда вы позже решите распечатать файл на экране, вам не нужно вообще изменять сопроводительную программу для чтения файлов, вам нужно только составить ее по-другому. Но обратите внимание, что эта проблема не имеет ничего общего с параллелизмом, это касается разделения проблем и повторного использования.
- 1. Совместимость Haskell - forkIO действительно недетерминирован?
- 2. ForkIO с состояниемT
- 3. Как составить forkIO и SqlPersistM
- 4. Haskell forkIO темы писать друг на друга с putStrLn
- 5. Использование sendFlush и sendChunkBS в forkIO
- 6. Написание примера серверного приложения - forkIO неизбежно?
- 7. `forkIO` и` putMVar`: что происходит под капотом?
- 8. Заброшенные сопрограммы
- 9. Сопрограммы в Numba
- 10. Вызов сопрограммы в asyncio.Protocol.data_received
- 11. Сочетание Сопрограммы и отражения в единстве
- 12. Есть ли способ в Haskell запросить состояние потока, используя ThreadID после forkIO?
- 13. Рекурсивные сопрограммы в C (C99)
- 14. Вызов сопрограммы из asyncio.Protocol.data_received
- 15. Зачем нужны сопрограммы в python?
- 16. Использование сопрограммы в качестве декоратора
- 17. Lua - сопрограммы - официальный пример
- 18. Как реализованы сопрограммы?
- 19. Ошибка сопрограммы boost1.53?
- 20. Haskell параллелизм поведение IO
- 21. Как объединить сопрограммы с отправителем и получателем?
- 22. py.test смесительные приспособления и асинхронные сопрограммы
- 23. async def и сопрограммы, какая ссылка
- 24. pylint, сопрограммы, декораторы и тип логического вывода
- 25. Зачем мне нужно ждать сопрограммы?
- 26. Языки сценариев, поддерживающих волокна/сопрограммы?
- 27. Где определяется тип сопрограммы Python?
- 28. Использование менеджера контекста с сопрограммы
- 29. Взаимно рекурсивные сопрограммы с asyncio
- 30. await asyncio.wait (сопрограммы) недействительный синтаксис
Это помогло бы, если бы вы сказали, какие сопрограммы вы говорите, поскольку они не являются частью стандартных библиотек. С другой стороны, они тривиальны для реализации в качестве библиотеки, поэтому они, вероятно, доступны в нескольких местах. – Carl
Возможно, также стоит предположить, что вы используете какой-то механизм связи, например 'BoundedChan' с' forkIO'. – rightfold