2015-05-20 19 views
5

Tôi đang cố gắng sử dụng kênh/STM để thực hiện truyền thông điệp trong Haskell. Có lẽ đây là một ý tưởng tồi tệ, và có một cách tốt hơn để triển khai/sử dụng thông điệp truyền vào Haskell. Nếu đây là trường hợp, hãy cho tôi biết; tuy nhiên, nhiệm vụ của tôi đã mở ra một số câu hỏi cơ bản về Haskell đồng thời.Haskell, Kênh, STM, đã đọc, Tin nhắn Truyền

Tôi đã nghe những điều tuyệt vời về STM và đặc biệt là việc triển khai trong Haskell. Vì nó hỗ trợ việc đọc và viết từ, và có một số lợi ích về an toàn, tôi đã tìm ra một thứ sẽ bắt đầu ở đó. Này sẽ trả về câu hỏi lớn nhất của tôi: không

msg <- atomically $ readTChan chan 

nơi chan là một TChan Int, gây ra một chờ đợi mà chờ đợi cho các kênh để có một giá trị vào nó?

Hãy xem xét các chương trình sau đây:

p chan = do 
    atomically $ writeTChan chan 1 
    atomically $ writeTChan chan 2 

q chan = do 
    msg1 <- atomically $ readTChan chan 
    msg2 <- atomically $ readTChan chan 
    -- for testing purposes 
    putStrLn $ show msg1 
    putStrLn $ show msg2 

main = do 
    chan <- atomically $ newTChan 
    p chan 
    q chan 

Biên dịch này với GHC --make -threaded, và sau đó chạy chương trình, và thực sự bạn nhận được 1 sau đó là 2 in ra màn hình. Bây giờ, giả sử chúng tôi thực hiện

main = do 
    chan <- atomically $ newTChan 
    forkIO $ p chan 
    forkIO $ q chan 

thay thế. Bây giờ, nếu chúng ta sử dụng - luồng, nó sẽ hoặc là in không có gì, 1, hoặc 1 theo sau bởi 2 đến thiết bị đầu cuối; Tuy nhiên, nếu bạn không biên dịch với -readed nó luôn luôn in 1 theo sau bởi 2. Câu hỏi 2: sự khác biệt giữa đã đọc và không là gì? Tôi tưởng tượng rằng họ không thực sự chạy như những thứ đồng thời, và họ chỉ chạy cái này sau cái kia. Điều này phù hợp với những gì sau.

Bây giờ, trong suy nghĩ của tôi nếu tôi có p và q chạy đồng thời; tức là tôi chia rẽ với họ, họ sẽ có thể chạy theo thứ tự ngược lại. Giả sử

main = do 
    chan <- atomically newTChan 
    forkIO $ q chan 
    forkIO $ p chan 

Bây giờ, nếu tôi biên dịch điều này mà không cần đọc, tôi không bao giờ nhận được bất kỳ nội dung nào được in trên bàn điều khiển. Nếu tôi biên dịch với -readed, đôi khi tôi làm. Mặc dù, nó là rất hiếm để có được 1 theo sau bởi 2 - thường chỉ là 1 hoặc không có gì. Tôi đã thử điều này với Control.Concurrent.Chan là tốt, và có kết quả phù hợp.

Câu hỏi lớn thứ hai: kênh và ngã ba chơi cùng nhau như thế nào và những gì đang diễn ra trong chương trình trên?

Ở mức độ nào, có vẻ như tôi không thể ngây thơ mô phỏng thông điệp đi qua với STM. Có lẽ Cloud Haskell là một lựa chọn giải quyết những vấn đề này - tôi thực sự không biết. Bất kỳ thông tin về làm thế nào để có được thông báo đi qua ngắn của serialize ~~> ghi vào socket ~~> đọc từ socket ~~> deserialize sẽ được đánh giá rất cao.

+0

Re: "Sự khác nhau giữa -threaded và không là gì", bạn có thể muốn [ giải thích của tôi về mô hình luồng của Haskell] (http://dmwit.com/gtk2hs). Bỏ qua các bit dành riêng cho gtk. –

Trả lời

8

Không ý tưởng của bạn là đúng - đây là kindof gì TChan s là cho - bạn chỉ bỏ lỡ một điểm nhỏ của forkIO:

Vấn đề là chủ đề chính của bạn sẽ không chờ đợi cho việc chấm dứt của chủ đề tạo ra với forkIO (see here for reference)

vì vậy nếu tôi sử dụng gợi ý đưa ra trong các tài liệu tham khảo:

import Control.Concurrent 
import Control.Concurrent.STM 

p :: Num a => TChan a -> IO() 
p chan = do 
    atomically $ writeTChan chan 1 
    atomically $ writeTChan chan 2 

q chan = do 
    msg1 <- atomically $ readTChan chan 
    msg2 <- atomically $ readTChan chan 
    -- for testing purposes 
    putStrLn $ show msg1 
    putStrLn $ show msg2 

main :: IO() 
main = do 
    children <- newMVar [] 
    chan <- atomically $ newTChan 
    _ <- forkChild children $ p chan 
    _ <- forkChild children $ q chan 
    waitForChildren children 
    return() 

waitForChildren :: MVar [MVar()] -> IO() 
waitForChildren children = do 
    cs <- takeMVar children 
    case cs of 
    [] -> return() 
    m:ms -> do 
     putMVar children ms 
     takeMVar m 
     waitForChildren children 

forkChild :: MVar [MVar()] -> IO() -> IO ThreadId 
forkChild children io = do 
    mvar <- newEmptyMVar 
    childs <- takeMVar children 
    putMVar children (mvar:childs) 
    forkFinally io (\_ -> putMVar mvar()) 

nó làm việc s như mong đợi:

d:/Temp $ ghc --make -threaded tchan.hs 
[1 of 1] Compiling Main    (tchan.hs, tchan.o) 
Linking tchan.exe ... 
d:/Temp $ ./tchan.exe 
1 
2 
d:/Temp $ 

và dĩ nhiên là nó sẽ tiếp tục làm việc nếu bạn chuyển đổi các cuộc gọi đến pq quá

+1

Có bất kỳ mô-đun/thư viện nào có thể đơn giản hóa điều này 'forkChild' /' waitForChildren'? – Bergi

Các vấn đề liên quan