如何使我的程序终止?

huangapple go评论58阅读模式
英文:

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 a thread blocked indefinitely in an MVar operation, which up to the documentation is due to the channel being empty. I guess getContents' 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 the stdin during the 5 seconds (i.e. whatever is in chan) 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 &quot;asynchronous&quot; 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 &gt;&gt;= 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 &lt;- newChan

  putStrLn &quot;start&quot;
  threadId &lt;- forkIO $ getContents &gt;&gt;= writeChan chan
  threadDelay 5000000
  putStrLn &quot;\nend&quot;

  killThread threadId
  a &lt;- 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&#39; cause a thread blocked indefinitely in an MVar operation, which up to the documentation is due to the channel being empty. I guess getContents&#39; actually never terminates.
  • Last but most importantly, the behaviour is different depending on the compilation parameters:
    • ghc -threaded main.hs &amp;&amp; ./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 the stdin during the 5 seconds (i.e. whatever is in chan) and then hang.

Just to clarify. Here are the ouputs:

&gt; runghc main.hs
start
abc     # user input
end
&quot;abc^C&quot; # ^C is done manually, to termiante the program and the last \&quot; is for formatting purpose

&gt; ghc -threaded main.hs &amp;&amp; ./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 &gt;&gt;= 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 &gt;&gt;= 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 &lt;- newIORef []
  putStrLn &quot;start&quot;
  threadId &lt;- forkIO $ forever $ do
    c &lt;- getChar
    atomicModifyIORef&#39; buf (\cs -&gt; (c : cs, ()))
  threadDelay 5000000
  putStrLn &quot;\nend&quot;
  killThread threadId
  a &lt;- reverse &lt;$&gt; readIORef buf
  print a

huangapple
  • 本文由 发表于 2023年2月10日 16:54:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/75408832.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定