I'm in ghci
testing some functions that fork threads, kinda like this:
myForkingFunction = do
tid <- forkIO (workerFunction)
putStrLn ("Worker thread: " ++ show tid)
putStrLn ("...lots of other actions...")
where workerFunction = do
putStrLn "In real life I'm listening for jobs...."
workerFunction
This results in:
🐢 > myForkingFunction
Worker thread: ThreadId 216
...lots of other actions...
🐢 > In real life I'm listening for jobs....
In real life I'm listening for jobs....
In real life I'm listening for jobs....
In real life I'm listening for jobs....
(and so on)
Then as I'm :r
-ing and iterating on the code, I notice that even when I reload, my workerFunction
is still going.
So, I thought could just look at the printed output and do killThread (ThreadId 234)
or whatever.
But first, I need to import ThreadId(..)
from GHC.Conc.Sync
?
Then I get this error:
<interactive>:110:22: error:
• Couldn't match a lifted type with an unlifted type
When matching types
Integer :: *
GHC.Prim.ThreadId# :: TYPE 'GHC.Types.UnliftedRep
• In the first argument of ‘ThreadId’, namely ‘805’
In the first argument of ‘killThread’, namely ‘(ThreadId 805)’
In the expression: killThread (ThreadId 805)
So I think I must be approaching this wrong?
I don't think there's any way to construct a ThreadId
(e.g. from the result of show
). See this question so you'll need to hold on to the ThreadId
returned from forkIO
, e.g. by changing your function to:
myForkingFunction = do
tid <- forkIO (workerFunction)
putStrLn ("Worker thread: " ++ show tid)
putStrLn ("...lots of other actions...")
return tid
and doing in ghci:
> tid <- myForkingFunction
> killThread tid
The error you get is a little confusing, and I believe has to do with the fact that numeric literals (like 216
) are polymorphic; the argument to ThreadId
is ThreadId#
which is an unlifted type so you get the error you see before the compiler can even complain of "no instance Num ThreadId#"
ThreadId
values are treated specially by the runtime. You can't just create one. You need access to a ThreadId
either as produced by forkIO
when creating the thread or by myThreadId
inside the thread.
Just for extra fun, threads don't get garbage collected while any ThreadId
from one is still reachable. So you can't just hold on to them in case you need them. You need to actually have a plan to manage threads.
I think you're probably best off using the async library and it's various linking operations to ensure that when the parent thread is killed, it also kills all its child threads. It's a little (or a lot) of an awkward code style, but it's the best way to get correct behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With