Reputation: 1457
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?
Upvotes: 5
Views: 865
Reputation: 48581
A ThreadId
is not really a number, although it has a number associated with it for identification. ThreadId
is a wrapper around ThreadId#
, which is a pointer to a thread state object (a TSO, or in the RTS code, StgTSO
). That object contains all the actual internal information about a thread. There is no way to turn an Int
into a ThreadId
(I'm pretty sure the runtime system doesn't even maintain a mapping from thread ID numbers to ThreadId#
s, and there's definitely no primop supporting such a conversion), so you can give up on that now. The only way you can killThread
or throwTo
a thread is if you hold on to the ThreadId
that forkIO
gave you. Note that killing a thread isn't usually the way you want to stop it. Normally you want to do something more controlled by communicating with it through an MVar
. Most people use the tools in the async
library to manage concurrency.
Upvotes: 4
Reputation: 27003
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.
Upvotes: 5
Reputation: 16635
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#"
Upvotes: 5
Reputation: 1
The problem is in the arguments of the killThread is wrong. You need to write killThread 234
instead of killThread (ThreadId 234)
. Have you tried that?
Upvotes: -6