英文:
Haskell. How to make my program to terminate?
问题
以下是您要翻译的代码部分:
I want to make an "asynchronous" reading of `stdin` for a few seconds. The way I am doing if is by forking `getContents` and writing to a [Chan](https://hackage.haskell.org/package/base-4.17.0.0/docs/Control-Concurrent-Chan.html). After 5 seconds, I kill the thread and read the channel.
From my understading, the code below should just print whatever is in `chan` and terminate, but it keeps waiting for input and `^C` must be pressed to finish. This is the behaviour you have when doing `getContents >>= print` on its own, so I have two guesses and no clue about each:
- The thread is not killed and `getContent` keep running asking for more input
- Something weird is happening with multithreading (see considerations below)
```haskell
-- OS: Ubuntu 22.04.1 LTS
-- Gnome Terminal
-- ghc 9.2.5
import Control.Concurrent.Chan ( newChan, readChan, writeChan, Chan )
import Control.Concurrent (threadDelay, forkIO, killThread)
import System.IO (getContents, BufferMode (..), stdin, hSetBuffering)
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
chan <- newChan
putStrLn "start"
threadId <- forkIO $ getContents >>= writeChan chan
threadDelay 5000000
putStrLn "\nend"
killThread threadId
a <- readChan chan
print a
Some considerations:
- Using
getLine
make it work only if Enter is pressed. But I'd like to just "stream"stdin
into the channel hSetBuffering stdin NoBuffering
is neccessary, otherwise the programm hangs (I guess waiting for end of input?)- Using
getContents'
cause athread blocked indefinitely in an MVar operation
, which up to the documentation is due to the channel being empty. I guessgetContents'
actually never terminates. - Last but most importantly, the behaviour is different depending on the compilation parameters:
ghc -threaded main.hs && ./main +RTS -N2
wont print anything and will hang until^C
is pressed (same thing with-N1
,-N3
, etc...)runghc main.hs
will actually print whatever has being thestdin
during the 5 seconds (i.e. whatever is inchan
) and then hang.
Just to clarify. Here are the ouputs:
> runghc main.hs
start
abc # user input
end
"abc^C" # ^C is done manually, to termiante the program and the last \" is for formatting purpose
> ghc -threaded main.hs && ./main +RTS -N2
start
abc # user input
end
^C # ^C is done manually, to termiante the program
So the question is simple. How do I make my program to end?
<details>
<summary>英文:</summary>
I want to make an "asynchronous" reading of `stdin` for a few seconds. The way I am doing if is by forking `getContents` and writing to a [Chan](https://hackage.haskell.org/package/base-4.17.0.0/docs/Control-Concurrent-Chan.html). After 5 seconds, I kill the thread and read the channel.
From my understading, the code below should just print whatever is in `chan` and terminate, but it keeps waiting for input and `^C` must be pressed to finish. This is the behaviour you have when doing `getContents >>= print` on its own, so I have two guesses and no clue about each:
- The thread is not killed and `getContent` keep running asking for more input
- Something weird is happening with multithreading (see considerations below)
```haskell
-- OS: Ubuntu 22.04.1 LTS
-- Gnome Terminal
-- ghc 9.2.5
import Control.Concurrent.Chan ( newChan, readChan, writeChan, Chan )
import Control.Concurrent (threadDelay, forkIO, killThread)
import System.IO (getContents, BufferMode (..), stdin, hSetBuffering)
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
chan <- newChan
putStrLn "start"
threadId <- forkIO $ getContents >>= writeChan chan
threadDelay 5000000
putStrLn "\nend"
killThread threadId
a <- readChan chan
print a
Some considerations:
- Using
getLine
make it work only if Enter is pressed. But I'd like to just "stream"stdin
into the channel hSetBuffering stdin NoBuffering
is neccessary, otherwise the programm hangs (I guess waiting for end of input?)- Using
getContents'
cause athread blocked indefinitely in an MVar operation
, which up to the documentation is due to the channel being empty. I guessgetContents'
actually never terminates. - Last but most importantly, the behaviour is different depending on the compilation parameters:
ghc -threaded main.hs && ./main +RTS -N2
wont print anything and will hang until^C
is pressed (same thing with-N1
,-N3
, etc...)runghc main.hs
will actually print whatever has being thestdin
during the 5 seconds (i.e. whatever is inchan
) and then hang.
Just to clarify. Here are the ouputs:
> runghc main.hs
start
abc # user input
end
"abc^C" # ^C is done manually, to termiante the program and the last \" is for formatting purpose
> ghc -threaded main.hs && ./main +RTS -N2
start
abc # user input
end
^C # ^C is done manually, to termiante the program
So the question is simple. How do I make my program to end?
答案1
得分: 4
以下是代码部分的翻译:
The thread `getContents >>= writeChan chan` is not an infinite loop that constantly adds content to `chan`. `getContents` creates a thunk, which is put in `chan`, and the thread terminates near instantaneously. Then in the main thread `readChan` gets that thunk, and `print a` forces it. It's the forcing of the thunk which prompts reading stdin, hence your program just blocks for more input until EOF or it gets killed.
What you want to do is to explicitly take small bits of input and write them into the channel. However, in the main thread, the channel does not give you a way to tell when it's ended. A workaround is to use an `IORef String` instead as a channel. Write to it by explicitly appending to the stored string, and `readIORef` will give you whatever content was written so far.
请注意,代码部分没有进行翻译,只有代码之外的文本进行了翻译。如果需要进一步的帮助,请随时提问。
英文:
The thread getContents >>= writeChan chan
is not an infinite loop that constantly adds content to chan
. getContents
creates a thunk, which is put in chan
, and the thread terminates near instantaneously. Then in the main thread readChan
gets that thunk, and print a
forces it. It's the forcing of the thunk which prompts reading stdin, hence your program just blocks for more input until EOF or it gets killed.
What you want to do is to explicitly take small bits of input and write them into the channel. However, in the main thread, the channel does not give you a way to tell when it's ended. A workaround is to use an IORef String
instead as a channel. Write to it by explicitly appending to the stored string, and readIORef
will give you whatever content was written so far.
import Control.Concurrent.Chan ( newChan, readChan, writeChan, Chan )
import Control.Concurrent (threadDelay, forkIO, killThread)
import Control.Monad (forever)
import Data.IORef
import System.IO (getContents, BufferMode (..), stdin, hSetBuffering)
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
buf <- newIORef []
putStrLn "start"
threadId <- forkIO $ forever $ do
c <- getChar
atomicModifyIORef' buf (\cs -> (c : cs, ()))
threadDelay 5000000
putStrLn "\nend"
killThread threadId
a <- reverse <$> readIORef buf
print a
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论