AFAIK TChan acts as a hub, every message sent is seen by others right ?!
i want a TChan that acts as a switch to send a message to specific thread, and also support broadcasting.
is there such thing ?

- 526
- 4
- 13
-
Can't you just use a broadcast TChan and discard the messages that are addressed to the other threads? – Mikhail Glushenkov Oct 18 '12 at 01:25
-
well i thought about it ,but it sounds inefficient to wake up 1000 threads and just one receives it – user1748906 Oct 18 '12 at 01:36
-
2Then I guess you should use a `Map ThreadId TChan` and do your own broadcasting. – Mikhail Glushenkov Oct 18 '12 at 01:47
-
is there a thread safe map in haskell ? – user1748906 Oct 18 '12 at 01:49
-
4Because Haskell data is immutable, most data structures are thread safe already. To modify mutable data from multiple threads, putting it in a `TVar` is probably the easiest approach. – John L Oct 18 '12 at 05:53
1 Answers
Edit: I re-read your question. This answer doesn't quite address "selective send", though it clarifies what a TChan
can do.
The "broadcast" approach described below will wake up all listeners (though on the bright side, it won't make 1000 copies of each item). To avoid this, use the Map
approach as @Mikhail suggested. I did this in my chat server example.
A TChan
is a FIFO queue:
writeTChan
adds an item to the end.readTChan
reads an item from the beginning.
For example, the following example forks 10 threads which fight over a single channel:
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
main = do
chan <- newTChanIO
forM_ [1..10] $ \i ->
forkIO $
forever $ do
x <- atomically $ readTChan chan
putStrLn $ "Thread " ++ show i ++ ": " ++ show x
mapM_ (atomically . writeTChan chan) [1..1000]
-- Wait for channel to empty out
atomically $ do
empty <- isEmptyTChan chan
when (not empty) retry
Here, each item is read by exactly one thread.
In contrast, the following example "broadcasts" a stream of items to 10 threads, by making ten copies of the channel using dupTChan:
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
main = do
master <- newTChanIO
forM_ [1..10] $ \i -> do
chan <- atomically $ dupTChan master
forkIO $
forever $ do
x <- atomically $ readTChan chan
putStrLn $ "Thread " ++ show i ++ ": " ++ show x
mapM_ (atomically . writeTChan master) [1..100]
-- Give threads time to complete
threadDelay 1000000
Now each thread gets all of the items written to the channel.
A couple subtleties to note:
Items written to a channel prior to
dupTChan
will not appear in the new channel. If we calleddupTChan
from the child threads rather than the main thread, somewriteTChan
s could happen first, meaning the children might not see all the items.Since nobody is reading the
master
channel, items written to it will pile up and will likely not be garbage collected. To avoid this caveat, use newBroadcastTChan to create themaster
channel.

- 41,996
- 18
- 86
- 115
-
your approach looks like erlang message passing style, each client has a private TChan and a map for broadcating (ets table in erlang). do you think erlang is better than haskell for chat server application ? – user1748906 Oct 22 '12 at 07:59
-
1@user1748906: I haven't done any Erlang, but I gather it has a really good concurrency model. If you plan to write a program that's primarily communication and concurrency, I recommend trying it in Erlang first. Even if you decide to port your program to Haskell later on, you'll take the ideas you learned from Erlang with you. – Joey Adams Oct 22 '12 at 19:05