- Imagine the scenario where multiple threads
need to access the same data.
So, here's a piece of data.
Thread one wants to access the data
and thread two wants to access the same data.
This is potentially dangerous.
One thread might be reading the data
while another thread is updating it.
So you can get like a race condition
and you can do this in Rust,
but then you have to use Mutexes.
There's a class of structure,
I should say called Mutex in Rust,
where you can, one thread can lock a piece of data
and another thread would then block
until the Mutex is released
and then the other thread can access, okay?
So it is possible to kind of simultaneously
access the same data, but it's quite tricky.
The preferred way is to use something called a channel.
A channel is like a pipeline between threads.
One thread can send data into the channel
and another thread can read data
from the channel without contention.
So it is very much like a pipeline
where one thread sends data
or transmits data into the channel
and another thread receives the data
from that channel, okay.
So it's like message queuing in memory.
So this channel that I've drawn here,
in order to create this channel, this kind of conduit,
there's a module called MPSE,
multiple producer single consumer.
That's what that stands for.
And in the multiple producer single consumer module,
easy for you to say, there's a channel function,
and it creates a channel,
and it actually gives you back a tuple.
The channel function returns the tuple
containing two objects.
It gives you back a transmitter object
which you can use to transmit data into the channel,
and also gives you a receiver object
which you can use to receive data
from the channel like that, okay?
So the channel function returns a tuple.
You can use the TX object to send data in to transmit data
and you can use the RX object to receive data
at the other side.
So that's quite a straightforward protocol really.
So channel returns a tuple.
TX is the transmitter object
and RX is the receiver object.
So you can receive a, you can send a single message
to a channel like this.
On the previous slide, we'd created the channel
and we got back a TX object.
So you can use TX dot send.
You basically sending a message into the channel.
I've chosen to send a string here.
Hei hei.
That's Norwegian for "Hello".
When you call the send function,
like a lot of functions in the A P I,
it returns a result enum,
and the result enum can indicate
whether the message has been sent successfully
or whether an error occurred.
So TX dot send returns a result,
which can be either okay or an error.
I'm assuming it's gonna work.
So I just call unwrap.
Okay? So that's assuming that the message
was sent successfully into the channel.
So this thread I've just shown you
will have used the transmitter object
to send a message into the channel, like so.
The main thread can then receive the message
from the channel like this.
The main thread, RX receive, the receive function,
will basically give us back the message afterwards.
And again, the receive function returns a result
and a result enum,
which indicates either an okay result or error.
I call the unwrap function.
I'm expecting this to work, okay?
If I was gonna be more tentative
then when I call the receive function
and I get back a result,
I'd use a match statement.
Match, okay, match error.
In this simple example, I'm gonna assume it's gonna work.
So I call the unwrap function
and it'll give you the actual value that was transmitted.
So up here I transmitted hei, hei.
That would be the value that I should receive
back on the main thread.
Alright? So it's a very simple
send from one thread, receive from another.
Let's actually run an example.
So the usual project, we'll have a look at main
and then we'll have a look at the demo channels
with a single message.
In the demo that's coming up next,
we're going to see how to send multiple messages.
Okay? So that's to come.
Let's take a look at the code.
Right? So, well, I guess I need to first of all,
uncomment the relevant bit of my demo.
Demo channels for a single message.
And here is the code.
So I, first of all notice that I've imported M P S E,
multiple producer single consumer,
created a channel,
gives me back a tuple,
it gives you back a transmitter object
that I can use to send messages
and the receiver object, I can use to receive messages.
Right?
So in my thread here,
I use the transmitter to send a message, hei, hei
and then unwrap the result.
While I'm here, notice the 'move' keyword again.
It's important that this object is owned by the thread.
Okay? So we have to, that object there has to be owned,
moved into the thread, like so.
So the channel now contains the message hei, hei.
The main thread down here,
using the receiver object that was returned,
basically my handle onto the channel,
using the receiver object,
I can call the receive method that returns a result,
okay or error.
Let's hope it works.
Let's unwrap it to get back the actual string.
Hei, hei.
And then I can say I've received hei, hei.
and we'll wait for, it's always courteous,
if you've kicked off another thread,
it's always courteous to wait for that thread to finish
before you terminate, just in case there's a timing issue.
So I think that should work fine.
Let's run it and see.
Okay then.
So here's my demo in demo channels, single message.
And I sent the message there.
I received the message.
Received hei, hei, so very straightforward A P I,
which is nice, simple is good.
Right, so what we're gonna look at now
is how to send and receive multiple messages.
So I've got a slightly different example now
where I'm going to spawn a thread,
which is going to send 10 messages into the channel.
So the channel, it really is like a queue.
You can send as many messages as you want
into the channel like that, okay?
And that's what I've done.
I've sent, I've got a string here.
It'll say message one,
it's gonna loop on 10 times,
and then it'll say message two, message three, message four.
So each string will be sent, it'll be message one,
message two, message three,
and eventually message 10.
So those are the messages are gonna be
in queued in the channel.
And then to receive them, just write the loop.
You can iterate over the receiver, okay?
This is a pattern that you see quite a lot
in other languages like Python.
Every time you iterate, it consumes.
So when you iterate over the receiver
it basically calls the receive function on this then.
Waiting for the next value to return.
This would be the value that gets returned.
Okay? So it'd be like message one will be returned
and then message two and then message three.
Okay? So have a loop to send multiple messages
and then have a loop to receive multiple messages as well.
Quite straightforward.
You might have actually guessed this,
if you had a inkling and it kind of makes sense.
So we'll run the demo for this as well.
Same project, same main file, but a slightly
different demo to allow us to send multiple messages
and to receive multiple messages as well.
Okay? So back in our code in main
let's comment that one out and let's uncomment this one.
Demo channels, multiple messages.
Quite exciting.
And here it is.
So create a channel,
get back a transmitter object and a receiver object.
In my background thread, 10 times, send a message.
Oh, I've got a delay as well,
just to make it so it isn't in, it isn't all instantaneous.
It's gonna delay.
And then send another message and then a one second delay.
And then it'll send the next message.
Okay? So as I send a message from one thread,
they're gonna be received from the other thread,
on the receiving side, it'll be receiving the messages,
it'll iterate over this receiver object.
Remember the receiver object you get back from the channel?
You can iterate over that, which basically means
wait for the next one until it's empty.
And then we, we terminate the application.
So this is going to work,
let's hope so.
There we are, so the messages that were sent
are being received over one second interval.
I'm actually gonna put a print statement
to say that I'm sending a message.
So I think here,
println, send in,
and then the S, "S" is the message we're sending,
like message one, that's the string that I'm going to,
I'm gonna echo it on the screen
so I can see when the message is sent
and when it's received to see how the sending
and the receiving kind of interleave with each other.
I feel I'm gonna need more screen space.
Okay, then let's do it.
So a message gets sent and received, sent and received.
So it's completely in sync.
One thread is sending a message
and the other thread receives it immediately.
So they are interleaved.
It's not like we send all the messages
and then it receives 'em afterwards.
They are working concurrently,
sending and receiving at the same time.
So that wraps up this lesson.
We've seen how to create threads.
We've seen how to wait for threads to finish.
We've seen how to capture state using implicit moves
and maybe an explicit move using the 'move' keyword.
And then finally, we've looked at the role of channels
as the easiest way to communicate data between threads.
Lovely.