Haskell/併發
| 讀者已經將本章節識別為未完成的草稿或提綱。 您可以幫助完善工作,或者您可以向專案室尋求幫助。 |
Haskell 中的併發主要透過 Haskell 執行緒實現。Haskell 執行緒是使用者空間執行緒,在執行時實現。與作業系統執行緒相比,Haskell 執行緒在時間和空間效率上都更高。除了傳統的同步原語(如訊號量)之外,Haskell 還提供軟體事務記憶體,這極大地簡化了對共享記憶體的併發訪問。
併發相關的模組是 Control.Concurrent.* 和 Control.Monad.STM。
也許比 **何時** 更重要的是 **何時不**。Haskell 中的併發不用於利用多個處理器核心;你需要另一個東西,“並行”,來做到這一點。相反,併發用於當單個核心必須在不同事物之間分配注意力時,通常是 I/O。
例如,考慮一個簡單的“靜態” web 伺服器(即只提供靜態內容,如影像)。理想情況下,這樣的 web 伺服器應該消耗很少的處理資源;相反,它必須能夠儘可能快地傳輸資料。瓶頸應該是 I/O,你可以在這個問題上投入更多硬體。因此,你必須能夠在多個連線之間有效地利用單個處理器核心。
在用 C 語言編寫的此類 web 伺服器中,你將使用一個圍繞 select() 在每個連線和監聽套接字上進行的大迴圈。每個開啟的連線都會附加一個數據結構,指定該連線的狀態(即接收 HTTP 頭,解析它,傳送檔案)。這樣的迴圈很難且容易出錯,難以手工編寫。但是,使用併發 Haskell,你就可以編寫一個更小的迴圈,只專注於監聽套接字,它會為每個接受的連線生成一個新的“執行緒”。然後,你可以在 IO 么半群中編寫一個新的“執行緒”,它依次接收 HTTP 頭、解析它併發送檔案。
在內部,Haskell 編譯器將把執行緒的生成轉換為分配一個小的結構,指定“執行緒”的狀態,與你在 C 中定義的資料結構相一致。然後,它將把各個執行緒轉換為一個大的迴圈。因此,雖然你編寫程式碼時就像每個執行緒都是獨立的,但編譯器會在內部將其轉換為圍繞 select() 或在你的系統上最佳的任何其他替代方案進行的大迴圈。
示例:並行下載檔案
downloadFile :: URL -> IO () downloadFile = undefined downloadFiles :: [URL] -> IO () downloadFiles = mapM_ (forkIO . downloadFile)
軟體事務記憶體 (STM) 是一種機制,允許對記憶體進行類似於資料庫事務的事務操作。它在多執行緒環境中程式設計時極大地簡化了對共享資源的訪問。透過使用 STM,你不再需要依賴鎖定。
要使用 STM,你必須匯入 Control.Concurrent.STM。要進入 STM 么半群,使用 atomically 函式。STM 提供了不同的原語(TVar、TMVar、TChan 和 TArray),可用於通訊。