Message passing is where threads communicate by sending each other messages containing data.
For this to be accomplished, Russ uses channels, which is a concept that the standard library provides
for us.
The channel has two ends a transmitter and a receiver.
The transmitter sends data and the receiver takes in that data.
A channel is considered closed when either the transmitter or the receiver end is dropped.
So let's take a look at how these channels work by implementing one where the transmitter will send
a string to the receiver who will then print out that string.
So the first thing we need to do is import an.
The crate that provides channels for us and that is standard sink.
MP SC And now to create a channel we will say
transmitter receiver.
So a tuple equal NPC channel.
Now we can spawn a thread that's going to say and we want to pass in a value.
So we'll say Let Val.
Equals string from
transmitting.
And now we can say transmitter send.
We want to send the string and then it returns the result.
So we need to unwrap it.
For our receiver to receive.
We just say let message equals.
Let me put this semicolon before I forget receiver dot receive and then this also returns a result.
So we need to unwrap it and now we can print out the message that we received.
And if we run this, we see that, we see transmitting.
So MP C stands for multi producer single consumer, which is essentially description of how channels
work.
So now let's look at how we can create multi producers.
But first, let's actually talk about ownership rules and message sending because they do help us write
save concurrent code.
So when we use this send function, it takes ownership of the value being passed in as a parameter and
then when receiver receives it, it takes ownership of that value.
And then obviously that result is returned back here to our message.
So.
There's nothing new with this ownership rule, but it's important to know that that is how ownership
is working inside these channels.
And Russ is doing this so that when we send a value, we don't try to reference or use that value later
on.
So an example of this is if we try to print out our values down here, and this is going to be a compile
time check for us like all the other ownership rules that way.
We cannot reference a value that no longer is valid.
So it is a compile time check, which is great for us.
But now let's take a look at how we can create multiple producers.
So I want to make sure I don't have that air anymore.
And.
Creating multiple producers is actually pretty easy.
So we can say let and this is a shortened way of saying transmitter and we will say transmitter clone.
And now we have another producer.
So I'm going to comment.
All of this out that way.
It's a clean slate.
And now we want to transmit a couple of messages.
So we'll say threads bond and then we'll have let vec equals.
And now we want a new vector to contain some strings.
Transmitting.
And I'm going to copy and paste this to kind of speed it along.
From.
Or original.
All right.
And then to transmit it.
Now that we have a vector, we'll say for each value and vec.
Transmitter Dot send the value.
And now unwrap it.
Because remember it returns a result.
And then we want to close it out.
Now let's format it to make it look pretty.
And now we want to do this again, but with the second producer we created.
So we'll modify that to X and then we will say the clone.
Is.
I can.
There we go.
Trans limiting.
All right, so now what do we have?
We have two threads.
Bond.
Our first producer.
It's going to transmit from original.
And our second one is clone is transmitting.
So now we want to set our receiver up to be able to receive all of these messages.
So we'll say for rec short, for received and receiver.
We want to print out all these values that we.
Received.
And now if we run this.
We see transmitting from original and now we see clone is transmitting.
So that worked exactly as we intended.
We had multiple producers and one single consumer.
But you might be wondering is what happens if our receiver can't keep up with the amount of messages
being put in by our producers?
Well.
The way it works is the messages are being sent.
And are getting put into a queue until the receiver is ready to handle it so the queue could become
overpopulated and when the queue is constantly being added onto and the receiver cannot keep up with
it very well, we actually start to waste resources because we are using resources, sending values
when we actually need to allow the resources to be more allocated to processing the data being sent
in.
So to alleviate this, rust does provide us with a solution called Sync Channel.
And.
What Sync Channel is, is instead of just being channel, we'll say Sync Channel, and then in here
we can put a value.
So I'll put a thousand and a sync channel is going to work the same as a regular channel, but we are
specifying how many values are Q can hold.
And then the send method becomes a blocking operation once the queue is filled up.
So once our queue reaches 1000 values, the next time we hit send, it's actually going to be a blocking
method until our receiver starts to catch up on the amount of values that are in our queue.
So this this in turn allows our receiver to be able to use resources more efficiently.
Remember, blocking does not necessarily mean we lose performance.
We could possibly see resource improvement without losing any throughput in our channel.
So this is something that I just wanted to make you aware of.