2012-08-24 35 views
6

Tôi có đoạn mã sau:hành vi kỳ lạ với đồng thời trong Haskell

import Control.Concurrent 

sleepSort = mapM_ (forkIO . put) 
    where put x = threadDelay (x*10000) >> print x 

này thực hiện một loại giấc ngủ trên một tập hợp các số nguyên, và hoạt động tốt, ngoại trừ một caveat:

Chương trình in ra mỗi số trong tập dữ liệu theo thứ tự, giống như nó được cho là. Tuy nhiên, sau khi nó kết thúc in ra số cuối cùng, nó chờ người dùng nhập vào một số, sau đó lặp lại số đó và sau đó hoàn thành.

Tôi không nghĩ rằng tôi đang yêu cầu người dùng nhập vào bất kỳ lúc nào, vậy tại sao điều này lại xảy ra?

+0

Bạn đang sử dụng GHCi? Nếu vậy, liệu nó có thể diễn xuất kỳ quặc theo một cách có vẻ như mất và sau đó lặp lại một số không? Nếu không, cái 'main' của bạn trông như thế nào? – gspr

+0

Nếu bạn đang thử điều này trong GHCi, hãy thử gõ 'print' làm đầu vào của người dùng. – Vitus

Trả lời

7

Điều này xảy ra vì chủ đề chính của bạn không chờ cho các chủ đề khác hoàn thành. Chương trình của bạn bắt đầu n luồng, nhưng chuỗi chính sẽ thoát ngay lập tức và bạn sẽ quay lại lời nhắc thông dịch viên. Trong khi đó các chủ đề khác tiếp tục sản xuất ra:

Prelude Control.Concurrent> sleepSort [1,2,3] 
1 
Prelude Control.Concurrent> 2 
3 

Bạn có thể khắc phục điều này bằng cách thêm một sự chậm trễ đến các chủ đề chính:

Prelude Control.Concurrent> sleepSort [1,2,3] >> threadDelay 10000 
1 
2 
3 

Nếu bạn đang chạy một chương trình biên dịch, nó sẽ chỉ thoát ngay mà không cần bất cứ điều gì in:

$ cat Test.hs 
import Control.Concurrent 

sleepSort = mapM_ (forkIO . put) 
    where put x = threadDelay (x*1000) >> print x 

main = sleepSort [1,2,3] 
$ ghc --make -O2 Test.hs 
[1 of 1] Compiling Main    (Test.hs, Test.o) 
Linking Test ... 
$ ./Test 
$ 

cập nhật: Thay vì thêm một cuộc gọi đến threadDelay-main, bạn có thể sử dụng một semaphore trong sleepSort chức năng:

import Control.Concurrent 
import Control.Concurrent.QSemN 

sleepSort l = do 
    qsem <- newQSemN 0 
    mapM_ (forkIO . put qsem) l 
    waitQSemN qsem n 
    where 
    n = length l 
    put qsem x = threadDelay (x*1000) >> print x >> signalQSemN qsem 1 

main = sleepSort [1,2,3] 
+0

Có cách nào đơn giản để chờ cho các chủ đề khác hoàn thành, hay điều đó thêm nhiều phức tạp? –

+1

@BenjaminKovach Xem cập nhật. –

+0

Cảm ơn bạn rất nhiều! –