Используйте переменную синхронизации для обеспечения атомарного доступа к ресурсу. Простой способ с MVar:
main = do
lock <- newMVar ()
forkIO $ ... lock
forkIO $ ... lock
Теперь, чтобы выполнить IO без чередования, каждый поток получает блокировку:
thread1 lock = do
withMVar lock $ \_ -> putStrLn "foo"
thread2 lock = do
withMVar lock $ \_ -> putStrLn "bar"
Альтернативным вариантом является выделение рабочего потока, выполняющего все операции putStrLns
, и отправка сообщений на печать через Чан.