2012-09-12 144 views
4

如果我使用forkIO創建線程,我需要提供一個函數來運行並獲取標識符(threadID)。然後,我可以通過例如工作負載,MVAR等。但是,據我瞭解,創建的線程非常有限,只能以SIMD方式工作,其中爲線程創建提供的功能就是指令。我無法改變我在線程啓動時提供的功能。我知道這些用戶線程最終由操作系統線程映射到操作系統。forkIO線程和操作系統線程

我想知道Haskell線程和OS線程如何進行接口。爲什麼Haskell線程可以將完全不同的東西映射到同一個OS線程?爲什麼不需要用固定指令啓動OS線程(因爲它在forkIO中需要)?調度程序(?)如何識別可能分發的應用程序中的用戶線程?換句話說,爲什麼操作系統線程如此靈活?

最後,有沒有辦法從應用程序中轉儲選定線程的堆?

+2

您可能有興趣閱讀http://community.haskell.org/~simonmar/papers/conc-ffi.pdf,它很好地解釋了多線程Haskell的實現。這有點舊,但仍然非常重要。儘管有這個頭銜,但它遠遠超過FFI。 –

回答

10

首先,讓我們來討論一個快速的誤解:

據我所知,這些用戶線程都最終被映射到操作系統線程操作系統。

實際上,Haskell運行時負責選擇哪個Haskell線程從其池中的特定OS線程執行。

現在的問題,一次一個。

爲什麼Haskell線程可以將完全不同的東西映射到同一個OS線程?

當前忽略FFI,所有操作系統線程實際上都在運行Haskell運行時,該運行時會跟蹤已準備好的Haskell線程列表。運行時選擇一個Haskell線程來執行,並跳轉到代碼中,直到線程返回運行時爲止。那時,運行時有機會繼續執行相同的線程或選擇另一個線程。

簡而言之:許多Haskell線程可以映射到單個OS線程,因爲實際上OS線程只做一件事,即運行Haskell運行時。

爲什麼不需要用固定指令啓動OS線程(因爲它在forkIO中需要)?

我不明白這個問題(我想這是源於第二個誤解)。你用一個固定的指令來啓動OS線程,就像你用一條固定的指令啓動Haskell線程一樣:對於每一件事,你只需給出一塊代碼來執行,這就是它的作用。

調度程序(?)如何識別可能分發的應用程序中的用戶線程?

「分佈式」是一個危險的詞彙:通常,它指的是跨多臺機器傳播代碼(大概不是你在這裏的意思)。至於Haskell運行時如何知道何時有多個線程,那很簡單:當您撥打forkIO時告訴它。

換句話說,爲什麼OS線程如此靈活?

我不清楚OS線程比Haskell線程更靈活,所以這個問題有點奇怪。

最後,有沒有辦法從應用程序中轉儲選定線程的堆?

我實際上並不知道任何在多線程應用程序或其他應用程序中轉儲Haskell堆的工具。如果喜歡,可以使用類似vacuum這樣的軟件包轉儲可從特定對象訪問的堆部分表示。我已經使用vacuum-cairo在過去取得巨大成功的可視化這些轉儲。

有關更多信息,您可以從我的intro to multithreaded gtk2hs programming中享受中間兩部分,「慣例」和「外國進口」,或許也可以參閱「無螺紋運行時」部分的部分內容。

+0

'forkOS'更靈活,您可以更輕鬆地與非haskell代碼進行交互。 –

+1

@PhilipJF那麼,你必須小心所有的多線程聲明,這也不例外。只有使用(OS-)線程本地狀態的API進行多次調用的線程需要從'forkIO'切換到'forkOS';所有其他人都有'forkIO'作爲一個完全不錯的選擇。 –

+0

是的。我可能應該說「稍微容易」 –

8

與其試圖直接回答您的問題,我會嘗試爲多線程Haskell程序的實現提供一個概念模型。我會忽略許多細節和複雜性。

操作系統執行preemptive multithreading使用hardware interrupts允許計算的多個「線程」在邏輯上同時運行在同一個內核上。

操作系統提供的線程往往是重量級的。它們非常適合某些類型的「多線程」應用程序,並且在像Linux這樣的系統上,它們基本上是同一個工具,允許多個程序同時運行(他們擅長的任務)。

但是,這些線程對於Haskell等高級語言的許多用途來說有點重量。從本質上講,GHC運行時作爲小型操作系統,在操作系統線程之上實現自己的「線程」,就像操作系統在內核之上實現線程一樣。

從概念上很容易想象像Haskell這樣的語言將以這種方式實現。評估Haskell包含「強制thunk」,其中thunk是一個計算單位,可能依賴於另一個值(thunk)和/或2.創建新thunk。

因此,可以想象多個線程同時評估thunk。人們會構建一個被評估的thunk隊列。每個線程將彈出隊列的頂部,並評估該thunk直到完成,然後從隊列中選擇一個新的thunk。操作par及其同類可以通過向該隊列添加一個thunk來「激發」新的計算。

將此模型擴展到IO操作並不是特別難以想象。我們認爲Haskell計算的單位稍微複雜一些,而不是簡單地強制純粹的thunk。僞哈斯克爾這種運行時:

type Spark = (ThreadId,Action) 
data Action = Compute Thunk | Perform IOAction 

注意:這僅僅是爲了概念上的理解,不要以爲事情以這種方式實現

當我們運行一個火花,我們期待爲例外「扔」到該線程ID。假設我們沒有,執行包括強制執行thunk或執行IO操作。

顯然,我在這裏的解釋一直很手動,並且忽略了一些複雜性。有關更多信息,GHC團隊撰寫了優秀的文章,例如Marlow等人的「多核Haskell運行時支持」。您可能還想看看操作系統上的教科書,因爲他們經常深入瞭解如何構建調度程序。