斯坦福 CS110 计算机系统原理笔记(三)
P13:Lecture 12 Review of mutex, conditional variable, semaphore - main - BV1ED4y1R7RJ
Okay, we might as well get started。 Welcome。 So the midterm is over。 I hope everybody did alright。
I'll have a couple, comments about that。 And then Stanford Shell。 I hope that's going okay。
As I said, there's like, if you haven't really gotten going on it, get going soon。 It's a。
those of you who did who have started it, you realize there's just lots of parts to it。
and lots of things to think about。 And there's been lots of Piazza questions。
lots of questions about the assignment。 So I hope that's going alright。 And we will。
have lots of officers this week。 And when did they say it was due Wednesday night or。
Thursday night? Did you say make it Thursday night? So you said, let's talk in a。
couple days。 We'll see。 So the midterm, a couple comments on that。 You should have。
gotten your midterm scores back。 If you didn't, please let me know so we can。
make sure that happens。 Overall, I was pretty pleased with it。 I think the, there。
were a couple questions on there that I knew would be difficult。 And there were a couple。
questions that I thought, hey, everybody should do alright on this。 And most people, did。
The answers to the file system stuff。 I mean, I think that was hopefully, relatively open ended。
but straightforward。 Most people did okay。 Problem one D。 Actually, problem one。
I think was the most challenging problem, which it was, kind of meant to be。 One D。
let's go kind of in reverse order here。 One D was the, question about, hey。
how do you deinterleave these outputs? Is it possible to, really deinterleave these outputs?
The answer is not really。 The way we set up the, input and output such that they had to track each other directly made it really。
impossible no matter what solution you came up with。 Unless you were allowed to。
collect all the data first and then pipe it to one, then pipe it to the other and, so forth。 And my。
the idea for this problem was you don't know how much data。
there is coming in and you're not allowed to store it all before you start piping。
away and it's not possible。 Very few people got that part correct for D, but that's, okay。
That happens on exams。 Definitely go and look at it。 If you do have, questions about, hey。
I don't really understand what the answer is all about。 Trust me。
we had some TAs during grading who were like scratching their heads going, oh, wait a minute。
what is this? And they did it all on the board and they convinced, themselves eventually。
So it was a difficult, difficult problem。 For the actual code, for that, two things。 First thing。
you know how we can use the pipe to and then you, have like FDS and then O, Clo, ex。
EC or whatever it is。 You know how you do that, and then you don't have to close some。
but you might have to close others or whatever。 We didn't even worry about that。 All right。
for the exam。 There were too many, like corner cases where you could have done one or the other and we just pretended。
everybody used O that pipe to and we pretended that everyone closed everything。
inside the children correctly。 There were two pipes you had to close correctly or。
two five the scriptures you had to close correctly outside of the children and that。
one we cared about。 But inside the children, if you went and if you said, oh, I'm not。
sure I got that right, that's okay。 We didn't worry about that。 The other thing。
that people tended to miss quite frequently was we didn't say you should wait。
PID for the children to end the program。 But let's just go look at the program。
the actual question again。 Okay, and or I should say let's look at the actual code。
that you may or may not have typed up to。 Remind me later。 There we go and then there we go。
Then two output。cc。 Okay, so you wrote all this for the actual pipes and so forth。
You did all that and you were writing where? You were writing in Maine, right?
You were writing the actual main part of this program which called the dual echo。
down here, okay, which called the dual echo down here and that was what you did。
And then Maine ended。 And Maine ended and if you forgot the weight PID, this is what。
it should have looked like by the way。 Let's see。 Two output。 Let's do the same。
example to do sort and then I'll pipe in a little test, test input。 Okay, so that。
was actually relatively nice in that it did sort and WC in the word count actually。
did come first but it didn't have to。 It could have come somewhere in the middle。
That was the interleaving part。 But you'll notice that the prompt didn't come back until。
after both children finished。 That's what you want in programs。 You want your。
programs to end nicely such that the prompt comes back。 Well, if we took out those。
two weight PIDs, right, let's see what might happen。 Okay, it's not guaranteed to happen but。
let's see what happens here。 I'll put, oops, put, there we go。
Two output and let's do the same thing。
before。 Let's do it up at the top of the screen。 There we go。 Okay, look。 So here's what happened。
Up here we got the prompt back and then the program went and then it just sits here like, that。
Is that a nicely behaved program? No, not very nice。 So we kind of expected you to go, oh。
if I'm writing the main and I want and I'm doing this two output thing for children。
you should wait for the children。 So I apologize to people who didn't pick that up but。
you kind of should get that that that's the what you want to do。 All right。 If you have, other。
there are regrade requests still open。 We're relatively strict about regrade requests。
and that you can't like for the pro and con when you go, well, I really meant this。
and try to explain away what you really meant。 If it wasn't on the page, that's it。 The other, one。
there were some people who were saying, ah, I wrote 125 words and you took off points and, whatever。
I kind of, you know, kind of warned you that the point, we thought those。
answers could be well within 100 words。 And so if you made, if you made a few more, we didn't。
care that much。 And if you do want to regrade requests, I'll go count your words and make。
a decision but like 120 probably too much in that case。 Question。 >> [inaudible] >> Good question。
The question is, hey, wait a minute。 I didn't know about this, pipe filling up business。
Is that the way right works or is that the pipe thing? It actually is, a pipe。
It's a built in Linux issue is that pipes can fill up。 They only make so much。
space for the pipe in Linux。 And so what all it does, it generally doesn't break things。
Although if you don't understand that like this code, it would break our code if we tried。
the D interleave because we never really talked about that。 No。 We didn't talk about that in。
the sense that there might have been a piazza question about it。 Maybe it was last quarter。
But the point is that the pipes can fill up。 What do they do? They just block until things。
lessen up again。 And if for some reason you're doing something walky like that program and。
trying to collect all the data at once, that can be a problem。 So that's kind of why I gave。
it on the exam was, hey, here's something new。 Oh, what do you think about this? Oh, now。
the pipes can fill up。 What do you think about it? That's kind of why I put it on there。
Is this what I mean? No, you didn't know the pipes could fill up。 No, what I'm saying is。
you might have known that。 The midterm said it。 Oh, yeah, yeah, there was a section。 Oh, sorry。
Yeah, sorry。 The Mitch, go back and read the midterm。 It did say that that was the whole, point of。
yeah, the pipes could fill up。 Sorry, you didn't read that question。
One of the pipes could not physically close the file。 No, good question to when the pipes。
fill up is a closed file。 No, no, it just blocks on it and says, oh, if you want to write。
more data, I can't accept more data。 I'm just going to block you from writing more data until。
somebody else reads and then you'll be able to block。 I'm doing it。 We're going to see an。
example of something similar to that in threading today where there's this, there's this, reader。
writer, kind of dance that has to happen where you've only got so much space and you need。
to figure that part out。 So good question on that。 Very good question。 Yeah。
A little more technical question about main。 Yes。
So in my phone, I have four loop。 You had a four loop。 It's kind of around this。 Okay, that。
probably could work。 Sure。 It's still such that I didn't get that。 Oh, put a regurg question。
if you think that's right。 That's a doing the weight PID in a loop where you check for all。
the children waiting is perfectly legitimate。 You absolutely could do that。 So put a regurg。
question for that。 Any other questions on the? I don't want to get into too many details。
about the exam, but if you do think we graded it incorrectly, please let us know。 As I said。
we're trying to be fair in the regrade process, but we're going to be, you know, we're going。
to be not going to just let you kind of redo your test and the regrade process。 You've been。
through that。 Okay。 All right。 And as I said, overall, pretty good on the midterm。 I think。
it was a challenging midterm, but overall people did all right。 And if you didn't do so great。
feel free to email me and we can chat about going forward and trying to pick things up, a little。
And you've also got the final and other assignments and so forth。 Okay。 All right。
So I know you're all thinking about Stanford Shell, which has nothing to do with threading。
but I did figure that last week you were all thinking, Oh my gosh, I got this midterm coming。
up and we went over, we went over mutexes and then CVs and semaphores and it was all wicked, fast。
And what I wanted to do is kind of go back and just kind of review what they are。 Let。
you get all the questions out that you might still have about those or new questions or。
and then we'll see a couple more small examples。 Okay。 I'm not going to actually do any like。
live like stuff today。 I just have some examples and we'll go over them as we go。 Okay。 So in。
threading, we have to have this ability to handle race conditions。 We have to have the。
ability for two or more threads to to either act in a similar section of code or act on a。
data structure in a way that won't corrupt the data structure。 And this is just something。
that you have to deal with。 Race conditions happen all the time when you're doing this。
threads just like, just like multi crossing。 We have a different, a few different ways of。
doing that。 We kind of walked through them last week。 The main one is, and this is the kind。
of the underlying one for most of these is a mutex。 Okay。 A mutex and we'll go over the, details。
I'll just kind of list through here。 The mutex is the first one we learned about, and we'll。
we'll see examples of that in a minute。 A conditioned variable, any mutex has。
no ability for two threads to signal another thread that they're done。 Right。 Now the。
signaling can happen by the fact that one thread will actually release the lock and the。
other thread will gain the lock。 So that is in some sense a signal, but it's not a, distinct like。
Hey, anybody who's waiting, go ahead and continue。 So there has to be some。
ability to do that if you don't want to busy wait or, or if you don't want to just have。
a regular straight up lock。 Okay。 This is particularly important when you have multiple, things。
trying to access a particular data, a particular like section of code, and it。
could be more than one。 You can't really handle that directly with a mutex。 So that's where。
a conditioned variable, any comes in。 And then there are all sorts of things you can do。
with a conditional variable, any that we happen to do a lot, namely waiting for permits and, saying。
Hey, here's a whole bunch of thread or a whole bunch of permits that a bunch of。
threads can work at once。 And so what we said was, well, let's, we can use a, conditioned variable。
any, but let's actually, let's actually build it up such that we。
build it into this other thing called a semaphore, which is a different type of, data structure。
Fairly easy to build has some nuances that we'll talk about later, today。 And it allows you to。
whoops, allows you to go ahead and build more structures a, little easier than a conditional。
a conditioned variable, any。 Okay。 So let's review these。 Okay。 Let's review all of them。 In fact。
the, let's see, here we go, maybe not。 There we go。 Okay。
So the mutex, okay。 And you can use these slides as kind of a reference to if you'd like。
The mutex is a, it's a lock。 Okay。 And it has two things you can do with it。 You can say。
either lock or unlock。 And what happens is when a thread takes this mutex and locks it, if no。
one else is locked it yet, it gets that and it moves on to the next line of code。 Okay。
And it's that straightforward。 If another thread comes in and attempts to use the lock, but the。
first, the first thread is still using it, then what happens is the, the second thread。
blocks until the other thread unlocks it。 Okay。 And that's the big details there that you。
should care about。 Okay。 You have to pass a mutex by reference or by pointer。 Why? Because they。
actually need to share the same mutex。 It can't be a copy of a mutex because then the data。
wouldn't be shared。 And that's how it works。 And by the way, under the hood, if you take。
a parallel processing class or you take maybe an operating systems class, they might talk。
about it uses atomic instructions under the hood to create the mutex so that two threads。
can't get it at the same time。 So there's a little more hardware support and operating system。
support for doing this。 But that's basically how it works。 Okay。 The thread obtains to lock。
and then executes the next line of code。 If it, if the other thread can't get it because the。
lock is already taken, the code waits and then until the lock is unlocked。 The only thread。
available to, the only thread allowed to unlock is the one that locked it。 Okay。 It's all。
it's undefined behavior if another thread tries to unlock a lock thread and it wasn't the。
one that did it。 Yeah。 Question。 Go ahead。 I'm listening。 I'm trying to get this tablet。
thing working。 Yeah。 You don't remember your question? Oh, okay。 What's that? Okay。 All, right。
So let's see。 I'm going to try this one more time with the tablet and see if it。
were。 Go ahead。 What's your question? Is that the boot waiting or is that like okay?
Oh, that is okay waiting。 Okay。 Good question。 The question was, "Is that busy waiting or。
is it okay waiting?" That is okay waiting。 But it is, but it doesn't always solve all。
of our problems。 Right。 So the permits part can't really do that one mutex or even just。
a few mutexes because as we saw with the traveling, or the traveling philosopher, I'm mixing my。
my big problems in the world with the, with the dining philosophers, you can't, you have。
to have the ability to say, "Oh, I'm limiting it to so many people being able to do this。
So many threads doing something with that。" So it's, you have to, you have to wait on, that。 Okay。
All right。 So。 Other question? Yes。 Eva。
What is the text look like in terms of boxers or friends? Sure。 The same problem。 [inaudible]。
It's an interesting question。 The question is, "What is going on with when, when there's。
locks and file descriptors?" Remember, a lock has no information about the rest of the, system。
Okay。 If a thread, if two threads happen to be working on the same file descriptor。
if one thread reads a bit, the other thread will read later in the file descriptor, but, there's no。
they, they could read at the same time and it would just interleave it as, as, is necessary。
But yeah, if you want to read only apart from one thread and then something, from another thread。
having a mutex is perfectly fine and you can order it that way if you want, to。 But yeah。
there's no, no other reason。 Remember, mutexes are relatively simple。
You either hold the lock or you don't and anybody, any other thread that tries to get the lock。
can't while you're holding it。 That's the, the whole business with a, with a mutex。 Okay。 All right。
Let's move on to, oh, let's see。 A couple other things about it。 Oh, you should。
try to hold a mutex for as short a time as possible。 Okay。 Why? Because if other threads。
are trying to access that lock and you're still holding it because you're doing something, else。
if it doesn't matter to your, if it doesn't matter to your logic that the lock is still, held。
release it earlier。 Sometimes it's inevitable and you have to release it, you know, later。
on because you're still using that data structure, but you want to really hold them for as short。
a time as possible。 Okay。 Let's see。 Deadlock, which is when two threads are kind of waiting。
on each other, that's not really possible with a mutex, with a single mutex because either。
one lock or one thread has it or not, it's not like they both can have it。 If you have。
multiple mutexes, that's when we start getting into the deadlock problem, which is why we have。
to go on to figure out other ways of doing this。 Okay。 So one nice, very nice helper class。
that's the easiest class in the entire world is this thing called a lock guard。 And a lock。
guard is as simple as this。 It has two methods, a constructor and a destructor, the constructor。
locks the lock, the destructor unlocks the lock and that's it。 There are no other functions。
no other variables, nothing。 Okay。 Just, you know, locks that are unlocks it and that's, it。
It takes a mutex as its parameter, so I guess that's the one variable it may hold as a reference。
to the mutex。 And what it's nice for is if you know that you are going to use a lock and。
then like leave a function or leave a while, the whole function, you might as well put a。
lock guard around it so you don't have to remember to unlock before you leave。
It'll happen automatically, for you。 And it's nice in cases where you have conditional behavior where maybe your。
insides on while loop and you return from that while you're great, the lock gets unlocked, for you。
You don't have to remember to do it。 Or if you're out, if you break out of the。
while loop and then leave the function, the lock, the mutex will get unlocked for you as, well。
This is a very nice convenience class to use。 If you know that you have to do that。
Sometimes you can't get away from it because you do need to unlock。 And again, don't keep。
don't put a lock guard at the top of your function just because it's nice to use。 If you。
know that there's other things you're going to be doing in that function that other threads。
might want that lock。 That would be a bad use of it。 Okay? But you'll see more examples。
of doing this。 Okay? All right。 That's a lock guard。 Now, a conditional variable, any。
This is the one that's a little trickier to understand。 Let's review what it does。 Okay?
It works in conjunction with a mutex。 So you have a mutex and then a conditional variable。
any has the ability to wait for a signal based on that mutex。 Okay? So, based on that, okay?
So basically what it does is it takes the mutex and you lock the mutex and then you。
do a wait on that mutex which is this conditional variable, any that uses the mutex。
The conditional variable, any, will push you off the processor because you're waiting for。
something to happen and then it will unlock the lock。 So it's very similar to SIG suspend。
in that case。 Okay? You can think of it as an analogous to that。 But the user should always。
lock the mutex first。 Then you're generally going to check some condition。 Okay? And you。
are going to, if the condition is met, you are going to go on to the next bit of code。
So this is where conditional variable, any, could be a little bit easier to use。 We will。
make it easier to use in a minute。 Okay? And when you get this notification, the, thread, the。
the wait function tries to reacquired that lock。 It may not be possible because two。
threads are waiting and if they both get a signal at the same time, which often happens。
when we do conditional variable, any notify all, CV notify all。 When those two get that。
signal at the same time, they race to acquire the lock。 One of them gets it。 The other one。
just continues waiting。 Okay? So that's the deal with conditional variable, any's, and, their。
their ability to, to do that。 And when the wait, on, when the wait comes out of the。
wait that you are not, you now have the, you have the lock again。 So when you get a signal。
once you get the wait, then you actually acquire the lock again。 So you should unlock it later。
Okay? That's how the conditional variable, any works。 We use this often to do permitting。
where you say I have these x number of permits and I have those number of, you know, y number。
of threads, which is more than x, trying to access this and I say only that many can do, it。 Okay?
And the general idea is you're going to have something you're waiting on, the, permits in this case。
while it's equal to zero, you're going to just keep waiting。 And the, the。
the other function will say, increment the number of permits and then notify everybody。 Okay?
That's how, and then that's when you would check, you would try to acquire the, lock。
you'd acquire the lock, you'd recheck the permits, see that it's one, go out of, that thing。
and then you would decrement the permits because now you've got your holding, one。
So the conditional variable, any, if you understand it, you go, oh, I see what's going, on here。
There's lots of, you know, lots of nuance to say, or lots of incrementing and。
decrementing because you're trying to keep track of this one variable, which is a permits。
type variable。 Okay? So that's, that's what you have to do。 That's what the conditional, variable。
any does。 Because of this weight being a really, or because of this while loop being, really common。
well, they built into a conditional variable, any, the weight into another variable。
or they built that, while loop into a second version of weight, which works like this, it。
takes the mutex, just like the regular conditional variable, any weight, and then it takes a。
predicate, which means a function that returns true or false。 Okay? That's the while condition。
and it actually does it in the opposite way, which is, you can read it like this。 Okay? And。
this is important。 If you read it, it basically this down here, and often we make that function。
into a lambda function because we can。 Otherwise, we'd have to call it as a separate function。
which sometimes is a little awkward。 But in this case, we just call, do it as a lambda, function。
We say like in this case, C V weight on the mutex, on mutex M, we pass in the。
number of permits so that we can access it。 This is in the capture clause。 And then we, say。
return permits greater than zero。 So you can say, wait until permits is greater than, zero。
That's basically what this is saying。 Okay? I like to use that, that, like, until, in this case。
wait until permits is greater than zero。 Because it's really while not。
permits is greater than zero, keep waiting。 That's kind of what it really is。 But in this, case。
we just think, I just think of it as like, okay, well, when permits becomes greater, than zero。
that's when I can exit this wait。 Okay? That's what we're looking for, the, untill part of that。
Okay? So that's how conditional variable n is work。 You'll see, those in lab this week。
and you will practice using those a bit in lab。 Okay? But again, you still have to。
with using these conditional variable n is you still have to do the things。
like keep track of the permits directly yourself。 Okay? And that's maybe or maybe not what you。
want to do。 If you don't want to do that, well, we have another way。 Okay? The next thing。
we're going to look at is the semaphore class。 So a semaphore is also relatively low level。
although for whatever reason, it's not built into C++ for us。 Okay? Why? Could be because。
it's actually really easy to build。 We'll take a look at the code again in a second。
But second of all, once we build it, we can use it and we will allow you to use it for。
the rest of the assignments。 We've already built it for you and I'll show you that。
But here's how it works。 It takes away all of that increment and decrement the permits。
and does it for you。 It's very nice。 Okay? It's really easy to set up a semaphore。 Okay?
You basically say semaphore permits five and that says there are five permits。 Okay? And。
if you do permits dot wait, what it does is it looks and goes, oh, how many of the five。
have been used? Oh, none of them? Okay。 You get one。 And it gives you one and then it。
decrements the permits。 Now four are available。 All the way down to zero。 When there are。
zero available, it just waits。 Okay? And then when you say permits dot signal, that。
is you giving up the permit, telling anyone else waiting for the permit to, waiting。
for the semaphore, I have released one, go fight for the one I just released。 Okay? And。
the only time that actually signal gets sent is when you go from zero to one because。
that's the only way it matters。 Otherwise it will always be able to get the permit。 Okay?
So that's how that works。 You can think of a mutex as a special case of a semaphore。
with one permit, kind of。 Okay? You shouldn't, in fact, if you go look at this up on like。
stack overflow, there's lots of people saying, don't think of it that way。 That's not a good, way。
It's not exactly the same。 Some people have said, oh, this is exactly the same。 It's not, really。
But you can think of it as being one permit, except that now you can actually do。
a signal when you're done with it。 And the interesting thing about a semaphore is you。
can signal from, well, you don't have to be the one acquiring the lock to signal。 Like。
that's the big difference there。 Mutex, the only person, the only thread that can unlock。
the mutex is the one that locked it。 That's kind of the other difference there。 Okay? Now。
a couple things we didn't talk about, semaphores。 What would a semaphore。
initialized with zero mean? What do you guys think? If I do the semaphore, initialized to one。
it would mean there's one permit。 And if I do a semaphore, two threads, fighting for it。 And one。
they both try to get it, one will get it, then when that one, signals, the other one will get it。
What would a semaphore with permits of zero mean? What, do you think, Cosner? [inaudible], Well。
so it's a good question。 The question is, would it just hang? If you had two threads。
trying to get there, it would, they would both wait。 Until what? What do you think? [inaudible]。
Only the parent can access the data。 Not quite。 You're on the right track。
Somebody else has to do something。 What do we say locks for mutex?
The only thing that can release the mutex is to unlock it, is the one that locked it。
Is that true for semaphores? No。 Anybody can signal on a semaphore。 Right? So this。
so you're getting there。 Anybody else want to take a step at what this, actually means?
This is actually an interesting case。 Yeah, go ahead。 [inaudible], Yeah。
you're waiting for some other thread to signal you。 And it doesn't matter if the other thread。
never needed to wait for that permit necessarily。 Right? It's just a way to say, hey。
let some other signal, some other thread signal me。 Is that what you're going to say as well? Yeah。
there you go。 Okay, so that's what it is here。 We don't have any permits necessarily。 Okay?
The permit wait means you just have to wait for a signal which can come from any other thread。
I'm going to show you an example of where this is another case of this where it's actually more interesting。
Yeah。 [inaudible], Yeah, any thread can say permits。signal。 Okay?
It doesn't matter that it didn't have any locked begin with。 It might be that your logic says, hey。
this thread, this one thread needs to wait for something。 In fact。
I'm going to show you an example in a second where it actually is useful。
But it might say this thread needs to wait for a whole bunch of other stuff to happen and it's going to。
one of them is going to signal it。 When it finishes, it just waits。
It doesn't have a permit necessarily。 It doesn't need to lock anything until it just needs to get the signal。
It's really what it needs to do。 Okay? I'll see that in a second。
My next question is what if you then it said permits negative number? What does that mean?
Let's look at the code too。 What are you going to say? What are you going to say? Yeah。
[inaudible], Yeah, that's exactly where that many different threads need to signal before it can actually do anything。
Let's go look at the actual code for semaphore。 I've got to remember where it is。
Let's see, slash user, slash class, CS110。 I think it's local source, I believe, and then threads。
There it is。 Okay。
Semaphore。cc and you can all go look at this。 Okay。 Let's look at how this actually works。
By the way, it's got a bunch。 It's got a CV in there。
It's got a conditional variable because that's how we built it。
Let's look at the weight and the signal。 Okay。 Weight does the following。 Okay。
Weight has this new text that it actually tries to acquire because it does need to update that value。
Okay。 So you have to have that lock in there in some case, in some sense。 Okay。
Then it does a CV weight and it does basically wait until the value is greater than zero。 Okay。
And then it decrements the value once it actually acquires that lock。 Okay。 That's what weight does。
What does signal do? Well, signal acquires the lock。 Okay。 And then increments the value。
And then if the value becomes one, it notifies everyone。 Okay。 Yeah。 [inaudible]。
MutexM is a class variable。 Yep。 It's a class。 If you look at, let's see if I can do this。 Actually。
we'll do it here。 Let's see。 It's there。 Include。
Some four dot eight。 There we go。 It is a, there it is。 It's a class variable。 Right。
So when you create this, when you create the class, now you've got the mutex in there。
So it's shared between the functions。 Okay。 All right。 So that's how signal and weight work。 Well。
notice that if the value, it's only, it's really only either incrementing or decrementing that value。
So if you start that value off at some negative number, well, it's going to take a whole。
but that number kind of minus one number of signals to get up to one to actually。
to actually notify the weighting threads that it's actually been done。 Okay。 So what might this。
what is a, an example of when you might need this? Well, what if we did the final?
Okay。 Let's say we had a program where we had 10 threads we're creating and they each need to do something。
And then one more thread needs to wait for it to actually proceed with its thing。 Now。
you could do something like join all the 10 other threads and then do the next thing if you wanted to。
but maybe you want to keep those threads going。 Maybe you don't want to。
maybe you want to do other logic that's going to eventually, you know。
do something else in those threads or something like that。
So what you can do in this case is you can say, let's actually go and create these threads and then each thread does its thing。
In this case, it's just going to do a see out。 And then it's going to send a signal on that seven for it。
And these, these thread has no idea which like number it's incrementing。
It's because look, I'm done with my stuff。 I'm signaling anybody cares。 I'm done。 And it signals it。
Right。 And then the read after 10 function here。 Well, it just waits。 And because we。
we started out this semaphore in this case, negative nine。 Okay。 Once there's 10 things that happen。
it gets incremented to one。 And that's when the signal, the way it happens。
And that's when the actual semaphore will allow the, like to continue。 By the way。
bring you back down to zero as it turns out。
But that's a way to do a signal like that through a thread that makes it so that you can do 10 things in a row。
And the last one gets to work last like that。 So let's try this。
Let's see。 My lecture。 Red。 CPP。 I think it is negative semaphore。 Okay。
So it's negative semaphore which should be exactly what I just said it is。
And there's where, by the way, there's where we're creating the semaphore negative nine。 Okay。
And then passing that semaphore is reference into all the other。
all the 11 threads we happen to have。 And negative semaphore, there we go。
So the threads, they all did in their own order, which they're going to。
Each one signaled after it got done。 And then when the last one received all those signals。
it proceeded。 And that's how it worked。 Okay。 So questions on the new kind of way we might use。
So we're going to see an example in a second。 A bigger example in a minute。
Alright。 Three different types。 Mutex, conditional variable, any semaphore。
We will use conditional variable any less frequently because we've got semaphore。
You will still use mutexes a lot。 You use those two much more than conditional variable any on its own。
Although you may have a reason to use that at some point。
Okay。 Let us talk about another pattern here。 So here's a pattern that's going to come up occasionally。
And this is going to leverage the fact that we can use like zero, not necessarily negative。
but we can use zero for our semaphore to go。 Okay。
It also can be kind of a way to not have to use thread join until you absolutely need to。 Okay。
So here's what we're going to do。 We're going to look at a program that has two threads that are both acting on a particular data structure。
One of the threads is writing things to it。 The other thread is going to read from it。 Okay。
And it's in fact going to read the data that was just written。 Okay。 It's very similar to a pipe。
Okay。 In fact, pipes may use some of this kind of under the hood as well。
But it's very similar to the idea of you're writing some data and another one's reading the data from that data structure。
Okay。 It's kind of like a web server and a client where the client to the web server like you type in www。
google。com。 That request goes out to the Google server and Google gives you back the data。
There's kind of a handshake there where you're requesting something and then the data has to come back。
It's kind of like that。 And we will do more of that。 If we get time today。
we'll see another example of that as well。 And as I said, it's kind of like a pipe。 Okay。
Here's the initial program that I will zoom up and we'll look at in a little bit of detail。 Okay。
How many people remember, well, you remember cues from CS106B?
You ever talk about like a circular cue or a circular buffer? You may remember those。 Okay。 Well。
some people do。 And if you took 170, you definitely don't know those because I know it's part of the assignments。
Here's what a circular buffer is。 Let me actually un-zoom and draw a little bit here。 I can。 Hey。
look at that。 So, one from last time。 Okay, clear。 Okay。
My horrible drawing skills will come back again。
Okay。 So a circular buffer is simply like this。 You can model it like this anyway。
You can think of it as a circle if you want to。 But a circular buffer is like if you start writing here。
the next place you're going to write is here, then here, then here, then here, then here, then here。
then here。 And then you just wrap around and continue to write here。
So you need a head and a tail more or less。 Okay。 In fact。
you can keep track of that in your program with just a mod operator to keep track of where in the actual circular buffer is。
So you can have the buffer partially filled or whatever。 The next place you write is here。
And let's say that one's filled, that one's filled, that one's filled。
I guess if you're next when you're writing is here, this was not filled yet。 And so forth。
But the idea is, let me erase that like this。 So let's say that everybody else up to here。
Let's say it was like this was filled。 Let's say that was filled。 That was filled。
We're about to write here。 We would then fill this one。
We would then fill this one and then come over here。 And this would be the next one。
Then we'd fill this one。 And then we would, the next one would be filled with over here and so forth。
If you ever get to the end where it's completely filled。
you should not continue to write to it because it's too full。 That's a circular buffer。
And it makes it nice instead of having to create nodes at each time and walk through them one at a time。
You just have one array and go through it in a circular way。 Okay。 Anyway。
that's what a circular buffer is。 So what we're going to do is we're going to write a little program or look at a little program that has this circular buffer where one of the threads is going to be doing the writing。
And the other thread is going to be doing the reading。
And it's going to hopefully keep track of who's writing where such that and reading so that it doesn't ever trample on the other one or read data that might not be available yet。
Okay。 So what does that mean in terms of this program? Well。
let's just look at how the program is implemented so far。 Okay。 We have a writer。
which basically is going to write random characters。
It's going to randomly add a character and it's going to randomly put it's going to put that character into the buffer one after the other。
And it's only an eight buffer with eight positions in it。 But we're going to do this 320 times。
Okay, so it's going to write right and then wrap around and write right right right right right right right right。
Now, if everything went correct went well, the reading and writing would happen at the exact same rate so that you could do that。
And then the writer might be a little bit ahead of a reader and they would keep going in that circular pattern。
That would be ideal。 Okay, but it's a very simple like thing that's happening here。
It's just taking that random character, putting it somewhere in the buffer。
And then when the i variable gets bigger than bigger than seven。
it wraps back around to the beginning and keeps going。
And it just does that 320 times putting the data in there。 Okay, that's what it does。
No one it's done。 It says or actually each time it says that it's put that data in there。 Okay。
The reader does the kind of the reverse。 It goes through and it reads the data one character of time and then just wraps around and keeps reading data。
Okay, so in a perfect world, the writer might be one or two steps ahead of the reader and everything would work perfectly。
Okay, and these are in different threads so you might think, oh, there's a raised condition here。
Ah, there is。
Let's actually look at it here。 Okay, this is called confused reader writer。 Okay。
And remember it's taking random characters and then writing them and then reading those random characters out。
So it's going to look a little funny here at first。
I will actually stop it while because it's doing that。
Let's look at what's happening here。 Okay, so the writer is ready to write and the reader is ready to read。
And the writer publishes S。 Okay, well the reader and then the reader consumes sphin。
What's going on with that? And then it says the reader consumes G and then the writer wrote in J and the reader consumed at。
Well, what's happening here is the reader is trying to read before the writer has ever even written anything and whatever the garbage was in there to begin with is what gets read out。
Okay, and so eventually and then it reads consumed data with like nothing and blah blah。
I can't even write it。 Eventually you might see some of the data getting in there but it's all garbled and the writer is writing over parts and it's not working the way we want it to。
Are they writing to and reading from the same file?
They're writing to and reading from the same buffer which is just a charged dog buffer。
You don't worry about any cursor。 You don't worry about any cursor。
We kind of are worrying about the cursor。 Let me go back to the drawing again。
Let me actually redo the drawing like this。 Here is the buffer。 Let's do it in a little more。
Let's say that one, two, three, four, five。 Let's only say there's six places。
Let's say that this is where the first read is going to happen but it's also where the first write is going to happen。
Let's call this the read head and the right head there。
Where is the last thing that could possibly get read? It turns out this is well。
like if you go all wrap all the way around but that's kind of a nuance。
But for now let's think about it。 Let's say that the writer tries to read first。
Well it's going to read garbage here。 So that's going to be garbage if it gets ahead。
Let's say the writer did get to write A。 Okay the writer writes A and then the writer writes B and then the writer writes C and D。
Let's say the reader is nice and starts reading now。
Well the reader will read A and then be here and the reader will read B and B there and then B there and the reader will read D。
et cetera。 And let's say E, F and then it comes around here and reads G。 Well good。
Let's say the reader for some reason stalls reading right here。
Well this is G and then this is going to be H and then this is going to be I。
it's going to be J before the reader ever reads it。 So that's the big issue here。
Okay if they're not in perfect sync they're going to be in trouble。
Okay now the perfect sync is actually a little bit nuanced as well。
To be perfectly sync how many things can get read before anything's written? Zero right?
You have to write something before you can read any of it。
So things can be written before something must be read。 The max number of things right?
So you could write all the way to the end here before going back but if the instant you go back if nothing's been read yet you'd better wait。
Okay so that's going to be the idea of what we're going to do with this program here。
Okay so anyway we go back and we look at it again and we go out。
So we go back to the completely garbled。 Nobody's in the right order。 It should have been。
it should have read SJCNN。 Let's see there's an SJCNN。
It did read it starting down there but eventually there's not necessarily in sync。
A bunch of stuff got read first and whatever。 Not so good。 Okay how can we fix this?
Well there are different ways of fixing it。 Okay you could。
you probably could use a couple of mutexes maybe if you wanted to and try to set it up like that。
That's the one way of doing it。 One, the way I want to show you is by using two semaphores。
Okay what we're going to do is we're going to, this is actually kind of what we just went through。
Why it's broken。 There's nothing basically, there's nothing that tells the reader that a slot can be read from yet。
And there's nothing to tell the writer that all the slots are full。 That's the big problem。
Okay all right so what can we do? Well we can have two semaphores。
Okay if we have an eight character buffer we can initialize an empty buffers to eight。
In other words it starts out with eight empty buffers。 There's eight spots that are empty。
So far so good。 Okay and then we can also set up a semaphore that says full buffers。
Well how many are full initially? Zero。 So let's do a semaphore starting at zero which is that weird case where it's always going to require a signal to at least get going。
Because it started with no permits available and it's, that's what's happening there。
Okay so we can do that。 We can set up the semaphore full buffers and empty buffers。
The full buffers, if you say a semaphore with no parameter it starts at zero。
Okay if you do an empty buffer, if you do the parameter here that's how many permits there basically are。
And this one says there's eight empty buffers。 Then you have to of course pass both the full and empty into both the reader and the writer because they're both going to be handling both reader and writer。
One's going to be waiting and then signaling the other and the other's going to be waiting and the other one and signaling the first one。
So that's where we're going to have to be working on。
Let's see how this works in the actual functions。 So what do we have?
All right we've got the full and the empty semaphores here。
Okay we are still going to do the same logic as before。
We're just going to do the for loop through the entire buffer。
The first thing the writer is going to do is it's going to wait on the empty buffers。
But we initialize those to eight and for a semaphore if the count is eight permits will it wait at all?
No it says great。 I can start writing。 And so boom it starts writing and then it tries to wait again and now there's seven available and boom it starts writing and that's that。
And we'll keep going。 After it writes a character it signals the full "hey there's a character I just wrote"。
So that increments the full counter in the semaphore to one and it signals because it incremented to one and it signals the other thread。
Hey there's something available I just put something there。 Okay。
All right let's look at the reader。 The reader does kind of the opposite。
It still goes through the loop and does it starts by waiting for full。
Well nothing's full at the beginning so reader has to wait at least one iteration of the writer putting something in there because the semaphore it's using starts out at zero。
Okay。 But then it immediately gets a signal once the first thing goes into the writer or runs the writer writes one and it originally immediately gets a signal and can read it immediately。
It might be just one step behind because maybe the writer hasn't written the next one yet and then it then it does that the wait again but it will do it immediately when there is a spot available。
And there may be many depending on how many the writer wrote in。
There might be there might be many in there up to eight。 Okay。 And then let's see。
And then it signals empty saying hey I just emptied one and that's when the the empty which the writer when it's waiting could end up going forward。
Okay。 So this one by the way will do this up to eight times without the reader doing anything。
And we can test that as we go and then it will it should work。
So this is now how the semaphores are going to be useful to us to keep everything in order。
And then actually look at that one。 This one is just called reader writer the correct one so I'll let that go in there for a bit。
And let me just zoom up to the top here。
Okay。 The readers or the writers writer writing the readers ready to read。 Great。
The writer published a well guess what the reader immediately consumed a。
And there's by the way I shouldn't have said this to the sentence before。
There's random sleeping going on here too so that that this is you know why it is just one after the other because there's some random sleeping going on here。
The reader got a in order and then why goes in and then why comes out and then W。 I。 Q。 W。
Well it must have been that the reader went to sleep for a while because the writer got a chance to put four new things into the reader W。
I。 Q。 W。 But what gets read out。 Oh good W。 I。 Q。 W。 Gets read out。
So we know it's going to be in order there。 And then it continues going through。
I'm not sure we get to a point where we might get to a point where eight things go in and it waits that long but we can we can do that but anyway it is definitely going to do it in order。
Okay。 Make sense。 Everybody why that's the case。 It doesn't go back and look at the code again and remember go look at the semaphore code and see how that actually works as well。
All right let's look at the code yeah。 Does it matter if you join the reader writer first。
Yeah good question does it matter if you let's actually look at the the actual code here because I didn't put that in the one main again down here。
Does it matter which one you join first I don't believe it does why because as long as they all get to finish then the buffers will be will be filled in that case right。
If you try to actually。 Let's see if you try to wait on the reader first。
There might get that the writer ends up let's see tried to wait on the read it might be might be that you have to do it in order just because you have to write before you can read and if you wait on the let's see if you waited on the reader first you might end up not being able to read it all and therefore。
I don't think about it more yeah I don't think about it more but I don't think it's actually a race just in that case the final waiting it might be I'll get back to you on that。
Yeah good question yeah。 Yeah so I mean we can try it I don't know that's a good we can absolutely try it but I don't know that it's going to prove our case will prove the negative but let's try it。
Okay and then read a writer there we go okay let's just see if it goes all the way through it would only happen near the end we would care because one would end before the other。
But no again not only think about it I don't think it's possible for if one ends and it's waiting for the other who cares if it's waiting for the first one and hasn't ended yet who cares so if the data hasn't come the other one wouldn't have ended yet so I think it doesn't matter。
I'm pretty sure I'll confirm that but it did finish so at least we know that it can work but again these sorts of things don't。
Let it let's do this though let's put in on just a little bit more waiting we'll put in let's see let's put a let's put us。
Let's put a little sleep for in here for like three seconds right before it even starts to try to read。
This is going to mean that the writer is going to try to put it's going to put eight things in there and then hopefully it will wait。
Let's see if that works。 Okay there we go so it's put one two three four five six seven eight things in there and then it went going after the reader finally woke up and I'm going to start reading some things。
That's how that works。 And the other way if we put the if we put it in the writer or rather in the yeah if we put it in the before the writer what's going to happen here。
What do you think if I put the sleep there what's going to happen now。
What do you think it should wait three seconds before doing almost anything right because the yeah I think that's a good good good answer。
Make reader writer and then we're going to there we go okay there everybody's ready then finally the writer starts writing up three seconds then boom it goes。
Okay nothing's ever written the reader can't bother to or can't do the reading in question。
Good question how does it get nine there let's actually run in this case and see what happens if we get a whole bunch of it。
Did it do nine or it might just be probably just a race condition would who would write in reading that's my guess。
It can't read nine without nine with it can't read nine a row because there aren't nine spots right but it's probably the last right the reads already printing out or isn't printing out yet or something like that so it's just a race condition there。
If it were like 20 I'd be a little suspect but I think eight or nine or ten not one two three four five seven nine yeah last one and it did do two S's at the end so that wasn't a weird。
So I think it's just that the it's probably just a race condition there it's also that maybe the writer's got a little more is writing a little faster than the reader anyway and the readers doesn't never quite catches up until the last part goes out finally I can read all the rest of the data。
There's no questions。 Do you have more questions? No you're sure? Okay。 Anybody else? All right。
So that's the that reader writer paradigm。 Let's reach out。 All right。
I think we have time to take a look we may not get to the whole thing but let's take a look at this thing called myth buster。
Okay this is a Jerry Kane special it's called myth buster because what all it does is it basically you ever log on to a myth and you go wow there's a thousand other people on here and it's really slow and it's kind of annoying and maybe it'd be kind of nice to know which myth has the least number of like CS one ten people slamming it。
Okay。 Well that's what this myth buster does。 Okay basically goes to all I guess 10 or 12 myth machines and it queries them using a kind of a wonky hack to actually see how many threads for people in this class are using that。
Now I also think it should check one oh seven or just in threads in general but whatever it checks this class。
Okay。 Now I'll show you it running before you anything else。 Okay。
Myth buster let's do it concurrent that's the one we don't love but okay。
Okay so holy smokes who's using all these processes。 What is this?
Machine and there's a little race cushion there anyway with a number of least oh no machine least loaded by CS machine number process and least loaded machine 74 so if you were to use it you would want to log on a myth 52 but I'm actually really curious myth 65。
As to say it's myth 65。 Myth 65 h top let's see。 Okay somebody's let's see so there's that let's see factor Python factor。
Who is this? Emma you have a hand。 Okay so you left a whole bunch of factors like going in your program possibly when you when you started。
I don't mean to call him out but she's happy to look good we could there are thousands of them so you said it's not alone I guarantee you that there there's some more。
Okay there's some more。 All right let's see if we can find a bunch more。
Yeah there's a bunch more so so yeah so you should kill all your processes so I literally when I when I ran this earlier I noticed some of these and I'm going to have to write a little note to next quarter make it so that every time you submit it just kills all your outstanding processes that are still running on whatever myth going you have because。
that's it so anyway that's you guys are all doing that and those poor 107 students can't use the myth machines because you're neither can you so Emma what you can do by the way is you can say I think kill all Python it will kill all your Python things on that。
that machine I believe you can try that and see if it see if it works and maybe it will do that and if we're all nice we can do that the good news the good news by the way is that all these processes while they are taking up memory they should all have tea under the status which actually means stopped so they're not actually spinning which is good so thank you for not like running spin。
But but yeah that the it they're generally not good to like happen sitting there doing nothing until the machine reboots that's kind of what's going to happen so anyway but anyway that's what myth buster does and myth buster runs somewhat slowly in that it has to pull each。
And so that's the concurrent ones this is the fast one the sequential ones ones going to be slowly that was nice fast one sequential one is going to be slowly because it has to do each one in order and it's just got to go and do this query and thing some of the myth machines by the way are down or like not available for us is aging into for whatever reason and it has a two second time out on that so it has to wait for that。
And so this mean well the concurrent one which was much faster did it in threads。
Okay just said oh I'm going to have a different thread asking this myth machine how many threads or how many processes and if it takes a little while great some other ones are still going to be running。
You'll notice it's out of order we could figure out how to order it a little bit better but you have 56 52 58 etc。
This one at least is in order and some are missing because it's not they're not like myth 57 is down or whatever but maybe it should just say myth 57 is down。
But the point is that you do it concurrently it's slow once you have threading you can wait for all those servers to reply to you or for a broken down you can do that。
So let's take a look at some of the code for this。
Okay here's the various data structures we're going to have。
I happen to make an idea list of all the student's soonets in the class。
And that's what it's asking for so reads that file and then it checks the Smith machine against all those names。
It just looks for the process numbers。 Okay it has a set of all this that's a set it reads that you can read this to file it's got a map that counts the processes for each for each myth to how many processes it has。
Okay compile the process count map is going to pass in those two details and in there we're going to do all the threading and then it's going to publish the least loaded data so that's how the program is going to work。
Okay let's see how I already mentioned all these things went over those things see anything important there that we need to know。
Nope I think we talked about it all。 The details are in a get num processes。
Okay this is where it actually goes and gets the for like myth 54 it gets the processes and then it checks them all against the students in the class based on their username。
Okay and this is how it would work in the sequential version。
Okay it's pretty straightforward basically goes through each myth。
Okay from 51 to 56 and then it counts number of processes in each one by calling this getting on process。
Well that's the slow part because you've got 10 different product 12 and processes 10 different myths and each one could be stalling。
Okay so that's what we're going to have to try to avoid。
This is what we showed you at working already。 Okay the point is that each call spends most of its time waiting around for the myth machine to respond to it。
Whenever you log in a myth and you're waiting well that's what your program has to do too。
Right it can't go on to anything else if you if you do it sequentially so that's the that's the big deal。
Okay so when you're counting these what you want to do instead is do it in friends。
Okay so let's see how this might work。 Well we're going to have account CS 110 processes again。
We're still going to pass in the num we're still going to pass in the set of students and we're still going to set pass in the process count map and this or I guess this time we're going to pass it in and not just get a return value。
Now we're going to have a mutex to lock on the process count map。 Why?
Because you don't want different threads trying to update the map at the same time。
Okay you might think well why does it matter if two threads with two different myths updates the map at the same time。
Well do you remember how maps are built under the hood back to CS 110 or 106 B days。
Remember how maps are generally some like maps are generally built if it's a regular old map you know it's a tree yeah and if you have a tree and if it's a balanced tree well that means that you have to actually move nodes around when you're inserting。
and that would be bad if two things tried to insert at the same time they'd be moving things around and it would go haywire。
And an unordered set is using a hash table which also might cause similar issues especially if you have hash collisions and so forth。
So either way you don't want two threads to try to write to that map at the same time so you lock around the map but the map writing is very quick。
Writing to the map or reading from it very very quick。
Okay so we can we can do that we can not worry too much about that。
Okay in fact we're just going to do a lock guard around it。
Okay we're going to change the up we're going to let's see we're going to do the actual count process or get number process in here。
Once we get the number of processes we're going to if the count is greater than zero we're going to update the minute count number and put it in the map。
And that's a very short this this whole section here is very short to update the map。
This is the part that takes a long time。 If you're trying to wait that's the part that takes a long time but if you're doing it in 12 different threads to 12 different machines it's actually fine。
Okay but you have to lock around updating that map for everybody。
And then we have permits in here this is so that we don't slam all the myths at once I guess it's not a big deal for 12 different myths having 12 different threads。
But in this case how many did we set up here did we did we say how many we set up we're I guess we're going to we're going to see how many so here we go。
So we're going to say only allow eight threads to go to time。
That's just kind of nice when you're doing networking things to limit it to some reasonable amount but we could get away with thousands if we really wanted to in this case but there's only 12 minutes or something。
So that's that。 Okay and then we signal on the end there we let's see when do we actually get the permits。
I don't see a permit's weight in there so。 Oh it's down here。 Let's see。
Oh right okay when we're actually send setting up the threads that's when we're doing it。
We will see a different model next week for a different model for this in this case this is where we're setting up the threads down here。
Okay so how are we doing that we're saying eight permits and we are going through and if we only we're only going to launch eight threads at a time when one of those threads。
Finishes it will signal this to start the next thread。
We are not joining on any threads until all the way at the end。
Okay those threads will end but we're only allowing eight to kind of go at the same time。
At some point there will probably be nine threads going at this time because the signal is going to happen and another thread is going to get spun up and then this one's going to close。
So in this case we could have an off by one but it doesn't matter but now there's nine threads instead of just eight。
Yeah。 Oh sorry yeah I didn't tell you about that one。
So in this case I guess there wouldn't be there probably doesn't after the thread just as a thread ends。
The semaphore allows you to say on thread exit do the actual semaphore releasing。
I don't know the magic that has to go into that we could probably look it up in the actual cut。
Let's go look it up real quick。 But yes if you want the if you want the signal to happen after the thread is when the thread is closing down that's what you would use for it。
Good answer on there good question on that。 User class CS110 local source threads semaphore。cc。
Okay let's look for on thread exit。
Okay here we go。 Let's see。 Yeah so it looks like there needs to be some p thread magic going on there to basically say hey do this when the threads exiting and you can do that。
So it's a little bit of a little bit of under the hood magic happening there with leveraging that we're using threads to do all this。
You do not need to know that for anything specifically just know that if you want to exit the thread or if you want to send that signal as the thread shutting down that's when to do it。
Good question。 The question is that thread won't shut down until join。
It will shut down it won't get cleaned up until join。
So in other words it still stops and it's no longer taking up any cycles on the processor。
It's just waiting the operating system just the thread manager is waiting to clean it up until you join it。
Yeah other questions on this。 Alright let's see。 Think。 Yeah let's see。
I think the myths concurrently。 What does that do for us?
That allows us to wait on the dumb flow network or the connection to happen。
Okay because we've got lots of them happening。 Your computer has the ability to send lots of messages to different machines all at once。
That's fine。 It's got its own waiting and de-interleaving happening with that。
But as far as you're concerned your program can send as many messages it wants to different servers。
In this case all the different myths at the same time through various threads。 Okay。
They semaphore limits the number of threads and yeah here's the on thread exit that we talked about。
It's an overloaded version of signal。 It schedules after the entire thread is exited so that makes it so there's not nine threads at once going。
Although maybe for a very short amount of time but not long anyway。 And that's it。
You can see the concurrent one versus let's do this。 Let's do it in two different windows。
So let's do it this way。 Alright so I guess it's a little it's interesting that I'm running this on the myths and checking。
all the myths so it's a little。 Okay I think we can do it this way。 Here's what I'll do。
Let's do it。 Yeah I want to make this window a little smaller。
And okay so we're going to run myth buster。 Let's do the sequential on this side and then。
Let's see。 Let's let's go。 Let's do the myth buster concurrent on this side。
Now try to hit return and then really quickly hit the other one。
We could time that if we wanted to but I said okay ready boom boom。
Okay and the myths buster sequential on is still going while the other one's already done。
Alright did it all on threads all once this is the other one still one after the other after the other after the other。
Alright what questions do you have about this stuff?
You see how this threading you're going to have to think about race conditions a lot。
There's lots of cool models that we can do。 Next week we're going to or this week and then next we're going to talk about some other cool problems where we're modeling bigger ideas。
The particular one is an ice cream shop kind of fun。
They were going to model that allows you to do threads that are waiting on other threads and using this whole idea of permits and so forth。
Question。 If you don't specify a myth it checks all of them in this case。 Like umber as a day time。
Oh if you just yeah yeah good question if you this is just about logging on to the myths in general。
If you just say SSH myth it will pick one there is a load balancer that's doing exactly what our program just did kind of。
Basically it says who is busy right now now I think it's kind of continually looking at who's busy。
It's kind of got connections that are open all the time going who's busy who's busy and it tries to send you to the one that's least busy。
Right it can't figure out if I've got all those crosses stopped though so sometimes that won't。
But no it definitely it's doing the exact same thing it's really doing more or less the same thing for you。
Yeah good question。 Alright any other questions think we'll get out here a couple minutes early。
Alright we'll see you Wednesday。
P14:Lecture 13 Ice Cream Shop Sitmulation - main - BV1ED4y1R7RJ
Okay, so we are starting out today。 Today is one problem。 Hopefully you've got the handout。
If you happen to break up here, I made plenty of them。
There's one program we're going to look at today and it is going to model。 Where did it go here?
It is modeling the ice cream shop。 We're actually going to try something live in person demo with people。
We have now opened up an ice cream shop。 I'm sorry for people watching the video。
You're going to not see the chaos that's about to ensue。 Here's what I need。
I need 11 volunteers or conscripts。 Come on up。 First three people get their big customers。
Which means you get ice cream if you actually buy ice cream。 Alright, let's see。 Okay, one, two。
three。 There we go。 Other people, the next people, you get to be a clerk。
Somebody gets to be the manager and somebody gets to be a cashier。
I think there's a couple of clerks。 Anybody else who is up here? There we go。 Okay。
thank you very much。 Clerks, go over in this corner over here。 Oh, and by the way。
everybody grab one of these too which is your instruction manual。 Okay。
there should be enough for everybody。 Okay, here's what we're doing。
We're going to model an ice cream shop。 Now what we're actually doing is we're modeling a problem that was given a believer in a lot of CS107 finally banned from many years ago when multi-processing and threading with the ANSES-1 upset。
It used to be。 We've changed things since then。 Okay, but here's what we're doing。 Alright。
we are setting it up。 So where's our manager? Please, okay, you're the manager。 You're going to be。
you're going to be, people are going to fight over you actually as it turns out。 Okay。
and where's our cashier? You're going to kind of come in near the end。 Okay, but you have。
there's a little number system here。 So the one, two, three, they, people will。
the customers will take the number in order。 So I've got numbers。 We have ice cream cons。 Okay。
and we have lots of, who are the customers? Customers over here? Okay。 So here's the way this works。
Alright, we're modeling this with only three customers。 Okay, a customer does the following。
Customer comes into our ice cream shop and says, "I want X number of cones。
" And you have how many cones you want on there。 Okay。
so they're going to come in and they're going to say X number of cones。
And what they're going to do is because they want to get their cones quickly。
or at least as quickly as they can, they're going to grab a clerk。 For some reason。
we have as many clerks as ice cream cons that are going to be delivered that day。
And each clerk gets to make one perfect ice cream cone。 In fact。
each clerk can try to make an ice cream cone perfect。
but the manager is going to keep them honest if it turns out to be terrible。
I used to have a friend who, he worked in an ice cream shop and he and a buddy of his used to have a competition to see who could make the smallest scoop without somebody complaining。
Which is really a mean thing。 You know the tiny little scoop and you're like, "Oh。
I'm just too nice not to complain。", Anyway, you don't get to do that。
You'll actually have more or less determinants。 It's a random thing。
but it's deterministic as far as the ice cream cons go。 Okay。
so they look at me and they will grab a clerk。 For the number of clerks for each ice cream cone。
Okay, each clerk will then actually make the clerks walk all the way over this table to grab an ice cream cone。
This is going to be a time thing。 There's going to be ice cream cones over there that you're going to make。
When you may have made an ice cream cone, you're going to walk back and you're going to present it to the manager。
Okay, and by the way, you are milling around at this point, waiting for your ice cream cones。
You're not getting in the way。 You're just kind of waiting for your ice cream cones。
When you have created an ice cream cone that you think is perfect, in other words。
you have grabbed one, you're going to bring it back to the manager。
And if there are two of you who are kind of going towards the manager at the same time。
you get to fight over who gives it to the manager。 Okay。
now you don't really need to participate in this fight。 Just basically grab one。 And, you know。
whoever you want, it's your kind of choice in that case。 Okay, and then the manager。
you are going to decide whether or not is a good ice cream cone。
Don't tell the clerks that you're not that。 The "e" means bad, the "g" means good。 Oh, nice。
Don't tell the clerks this。 Okay, so anyway, and then you're going to either tell them, "Yes。
that was a beautiful ice cream cone。 In which case, you are going to say。
"Now I have made one of my six ice cream cones that I have to make today。", Okay。
once you have made it, you might want to keep track。 You can say, "I'm going to make six。
and you can say five, and four。 Once you make all six days that are good, or that you have approved。
you get to go home for the day。", Okay, all right。 Clerks, if the manager says, "No。
that wasn't a good cone," you have to throw that cone away。
just throw it around the floor wherever you want, go get another cone。
and come back and fight again。 Okay, for the manager's attention。 Okay。
customers are just waiting around。 Now, if the manager says, "This is a great cone, perfectly fine。
" you can go deliver it to the customer and then you get to go home。 Okay, all right。 And now。
once you get all three of your cones back, you come take a number, okay。
and the cashier is waiting for somebody to take care of。 Okay, now。
the minute you see somebody take a number, you can go, "Oh, I'll take number one。"。
And then take as much time as you want, checking them out。 Make sure they pay you in the right。
whatever you want。 All right, but you have to check them out。 Once you check them out。
you get to mark off, but you have checked off one, customer。 You have to check off three customers。
then you get to go home。 Okay, all right。 And then, customers, when she's checked you out。
you can then take your ice cream and go home。 Okay, at the end of that。
you should have however many ice cream comes you get。 Now, does everybody。
that was a lot of stuff all at once。 Okay, you have your own instructions here。 Okay, in fact。
I will pull up the instruction so that everybody can kind of see what's going on。 Let's see。
I'm going to do this。 I'm going to pull this up。
Here it should be here, and then ice cream details。 There we go。 And if this pulls up correctly。
we should do that。
And there we go, hold on, not now。
And here are the instructions。 I will make this as big as I can。 And there we go。 Okay。 So。
I know there's a lot there, but you can kind of see what's happening, right?
The customer grabs an ice cream cone, grabs a clerk price cream cone, mills around。
the clerks do their thing。 When the customer gets back the ice cream cones, they go to the cashier。
take a number, et cetera。 Everybody get what's going on more or less? I mean。
it might look a little crazy。 Let's see if we can make this happen without, no real fights, please。
But you can see what happens。 Okay。 So, customers。
come on into the shop as much as whatever you want。 Okay。 And go buy your business。 Okay。
Clerk's over there。 All right。 I will now, I will marry for the people listening on the video。
We are the ice cream cone customers are asking the clerks, they're grabbing the clerk。
A clerk is going over for the ice cream cone。 Clerk goes to the manager and says。
is that a good con? Yes, it is。 It's a good con。 All right。 So。
we got a good con and you give it back there。 Okay。 And then now there's some people milling around。
but there's a little, oh, a bad con。 We got a bad con in there。 Okay。 That is a good con。 Okay。 So。
you can see there might be a little bit of a bottleneck here。 This is a little bit less than that。
Oh, no。 We got more bad con。 Okay。 Oh, bad con。 Okay。 No。 You got a good con? That was a good con。
Okay。 Okay。 Okay。 All right。 So, all right。 So, then the, has the cashier had anything to do yet?
One person。 Okay。 And so, you have gotten all your cons。 Do you have just one con? Yes。
Just one con。 Okay。 All right。 And so, you get to take a number。 Okay。 That's a good con。 All right。
And let's see。 Two。 There's number two。 Okay。 So, one person is still waiting for the cons。
And have you done all six cons? I said all six。 Okay。 You can go home。 All right。
Manage your just to go home for the day。 Clerk, so。
if you take and grab all your cons to this point, yeah, you're going to all go home。 You're great。
Good to go。 Now we have that and you have now taken care。 Okay。
And the cashier is the last one to go home。 Sadly。 But, but that's that。 Okay。 And you're all done。
And you got your cons。 Okay。 Now, that was a bit of a melee up here, right? No fights。
I didn't see any real fights。 But did you kind of see that this is the thing we're about to try to model all of that madness in one program?
Okay。 That's what we're trying to model here。 And you have the program and there's a lot to it。
But we can we'll go through it one little part at a time。 Okay。
So questions on what happened up here。 Did anybody see anything that was odd or didn't get or whatever?
Getting quite figure out what was happening。 That's okay。 Yes。 Okay。
So a customer asks one person and then they all wait。 Like。
because like technically only one degree you get like a message and then all of them wait for them。
Yeah。 So okay。 Okay。 Well, first good question was look I'm confused on like the mechanics of how this is going to work like programming wise like getting it。
So it was all happening kind of in parallel。 Each customer was able to ask for however many cons by telling a clerk one clerk at a time。
please make me a con。 And that was what was happening。
So one customer went to a clerk and said make me a con。
And for some reason we had a number of clerks one per cone, which is a weird model。
So I will grant you that this model of an ice cream store is a little bit weird and probably wouldn't work in real life。
And you could certainly make it better。 But that's the model one person who wants any number of cones goes to any number of clerks and says make me one con。
And then they go and ask the manager and the manager is saying that good cone bad cone and the clerk has to keep making cones until they get a good cone。
Emma, you probably made three cones or ever before you got a good one or whatever。 Right。
So I thought I'm going to come back and forth a couple times and scowl on her face with a number of cones she was making。
And then the cashier had to get the now notice that the clerks were kind of fighting with each other for the manager's attention。
Fine。 The customers were not fighting for the cashier's attention and you probably want it that way。
Like even in a real ice cream store, you'd rather have the customers go when they get up to the line go on next and then they get handled next。
As far as the clerks and the manager go well they could kind of all mill around until one gets to decide。
Okay。 Do we see the kind of basic idea though of this? Okay。
Now we're going to try to model it using threading techniques that we hopefully kind of understand now but we will go through them。
Okay。 We're actually going to go through five different types of things here with this program。
It is kind of a meaty program。 I can't really imagine how it ended up as a one final exam problem。
Maybe it was two but but anyway that's the way it goes。
We're going to talk about a binary lock which should be at this point。 Hey do a mutex。
Just going to be a binary lock。 We're going to do a generalized counter。
That should be thinking maybe semaphore。 You should be thinking about that。 A binary rendezvous。
A binary rendezvous is when basically well we'll get to it but it's two two threads trying to coordinate between each other。
Like for instance a clerk and a manager trying to make a decision like hey the clerk has to wait for the manager to tell them whether the cones good or not and the manager has to wait for the clerks to come over and so forth。
So that's going to be something we're going to do。
A generalized rendezvous is when you have multiple things at once happening。
This could be with let's say the people who are asking for the ice cream cones have to wait for all their cones to be made。
They're generalized like I'm waiting for a bunch of things to happen kind of before I can do anything。
And then layered construction is more or less it's more or less like how do you do this one thing on top of the other。
We'll do that。 And we'll see how that works。 You certainly can go download the code。
You have the multi-threading all the code right in front of you。
So if you're looking at one piece and you want to go back and go hey wait how did that work with the other part feel free。
And I know you haven't had a chance to really look through this but we will go through it one thing at a time。
Okay so binary lock what's the binary lock it's a mutex basically。
Okay to remind you a mutex does nothing else except allow two threads to try to get into some critical region。
And it doesn't even have to be the same critical region they just have to basically both be fighting over some resource。
One of only one of which can do can access that resource at a given time。
Many times it's a global variable or some shared variable they both want to update and you want to do it what we call it。
Okay and then so again it's all about single thread access。
Okay the generalized counter this is where we talk start talking about semaphores。
This is where the counter itself the semaphore itself can do a can increment a variable atomically。
In other words no other like only one thread can actually make it do its thing at once do the incrementing or decrementing。
Okay we talked last time about the various things we can do with a semaphore。
If we have a semaphore that has a count of zero like no permits it's basically just uses a signaling thing back and forth。
Okay so one thread can signal another thread which is waiting on that it's not like there's a permit here just one thread goes on waiting around。
So we will see those used as we go as well。 Okay and this is where you're coordinating some limited resource that has some number of things in it。
Number of columns or one column or you know one sort of number of things that you might want to link。
Okay all right a binary rendezvous this is where again you use semaphore and this is for inter thread communication。
Okay and the example here is a pretty good one。 So suppose we had thread A that needs to know when thread B finishes something。
Okay so for instance when the manager has to determine whether or not the cone is well good or not the clerk has to wait around for that to happen and has to wait on that other thread to do to do their thing。
Okay what we can do is we can have this rendezvous semaphore initialized to zero because all we care about is the signaling part。
Okay and thread A actually waits on that semaphore。
Okay and after thread B finishes it signals thread A which continues。
Okay and thread B does not care about anything else that the other thread is doing at that point。
It just goes signals and then moves on。 Okay so that's how a binary semaphore works or a binary rendezvous in this case。
Okay there's only one event that needs to happen and that's all we care about。
Okay this is sometimes used to wake up other threads like a thread just waiting around and then somebody wakes it up with this signal。
That's a good way of thinking about it。 You can do a bi-directional rendezvous。
This is different than a generalized one。 This is like basically one thread waits for the other which waits for the other。
You have to be very careful that you do that so they're not both waiting at the same time because then you'll get deadlock。
So you have to be careful there。 Okay so there's some logic that you need to figure out there。
Okay all right a generalized rendezvous is where you have a binary rendezvous and a generalized counter。
Okay this could be more or less the like ice cream customers are waiting for the clerk to do something or actually waiting for a bunch of clerks to do something and they have to sit around。
So in this case thread a spawned five thread B's。 That sounds like the ice cream customer asking five different clerks。
Okay thread a spawned five thread B's and needs to wait for all of them to make a certain amount of progress before advancing。
That's when we would use this technique。 Okay again you have the semaphore initialized to zero。
When a needs to sync up with the other ones it will block until they all finish。
Okay and then when they all finish then they will the thread a will then be able to get moved forward after that。
Okay this is a good this is the generalized part of this is you have some task that you're dividing up。
You're saying I need to make three cones I'm dividing the three different clerks you need to generalize that and then wait for all those tasks to complete。
That's the generalized rendezvous。 Okay all right and then this whole layered construction is basically using all this together to say oh we've got a mutex and then we can have a semaphore that uses the mutex and how do you kind of piece them together。
You've got some global counter that uses this that's going to have mutex associated with it you've got to wait around what size the counter changes value or reaches some value and so forth。
So that's the big takeaway for that one is that you need to you need to be able to use these constructs together to suit your to suit whatever you're trying to do。
Okay all right let us look at some of the actual code。
Okay I've already we've already talked about this we modeled it pretty distinctly okay ice cream store clerks manager customers cashier lots of clerks one manager one cashier。
Customers are in a hurry so they get lots of clerks per one per ice cream cone and then once they get their cones back they go to the cashier I mean it's not a terrible model。
And then each clerk just gets to make one cone yeah that's a little weird but that's the way it works in this in this world and the manager has to determine whether or not the cone is legitimate or not。
Okay and the big issue there for the cashier is it has to be a first in first out queue otherwise it's chaos because the customer's going I was here first and why didn't you take care of me and so forth and Brenda would not want to do that because she could help。
Okay and then we're actually have to at some point determine when everybody goes home it's a little wonky like that the manager knows the manager has to make six cones by the end of that little weird so we could have done it some other way but in this case we just say right off the bat we'll do that。
You will see how the code ends up manifesting itself for that。
Okay all right questions before we start looking at the code about the overall picture here you've seen it now we're going to see some code you kind of get the idea。
Okay good all right so here's the various things we're going to look at。
Okay we're going to look at all these parts of the code you have them in here I'll try to tell you which page these are on as we go through them because they're not necessarily in the order on the code in the code。
But the first thing we're going to look at is the random number generators just to kind of see where it is this is actually on page four of nine in the handout。
And I've got it right here as well。 Basically we have lots of randomness going on here why because we want to make it look like it's some sort of like actual time constraints and it's not always the same time。
Okay we do this often with these sorts of things this makes debugging a little harder because it is kind of random but it will definitely test lots of different categories。
Okay most of these are times we have get number of or get actually a couple of mark get number of cones like the customer comes in and says I want you know how many cones so that's not a time。
There's get browser browse time which is the customer milling around there is or and that's part of it。
There's get prep time that's how long it takes the clerks to make an ice cream cone that may or may not be good。
And then there's get inspection time along manager takes and then finally there's get inspection outcome which is the binary yay or nay as far as the cone goes。
Okay relatively straightforward not anything you need to particularly concern yourself about it's just using a library function we actually wrote to get these values out。
Okay let's look at the structs here so this model is if you're in 106 B this or 106 A you would probably cringe if you saw this that there's some global struct here。
All right why because we don't necessarily like global things but with threads sometimes it just makes it easier to pass around global very or to have a global variable then to deal with having a having a struct that you're passing around。
For encapsulation reasons we probably would want to pass it around in a in a more robust program but for now we are just making a struct called inspection and it's got these fields in it。
Okay it's got a mutex for available this is the handshake basically between it's the it's the binary round of view between the clerk and the manager。
Okay so there's an available mutex which is basically saying only one clerk can be dealing with the manager at a time the manager can only look at one ice cream cone at a time that's the mutex。
Okay there is a semaphore for signaling hey manager I've got an ice cream cone for you and there's a semaphore for saying hey clerk here's your ice cream cone it's terrible or it's great。
That's another one and then there's actually a boolean in here for whether or not it passed and then by the way out here we create a struct called inspection and then we immediately declare it as a variable inspection。
Seems like it's overloading the name but that's the way it goes so this is a global variable again we didn't have to do it globally but just makes it a little easier。
Now there are a couple interesting things about this if there's only one value about past or not right how many things can be using this struct at any one time。
One thing right one manager or one one manager or one clerk can be inspecting this or using it at one time there's only one manager which is nice。
In fact if we did do many managers like let's see what multiple managers or assistant managers or whatever then we would have to rethink this we might have to have some other either array of these inspection structs or something else because the way it's worked right now there's one value at one time which says whether or not ice cream cone is good or not。
Okay, all right so that's how the struct works what questions you have about that struct at this point maybe you don't。
We'll see it in action very soon。
Okay there's another struct which is the checkout one so we're kind of jumping ahead to the checkout phase but the checkout has a semaphore for next in place in line。
Okay which is a new kind of integer here it's an atomic unsigned in what that means is that that variable can be updated by multiple threads at the same time and it will never do that we're double or not not double counting。
So atomic such that it makes it so the plus plus works no matter 10 threads come at it once it's what we call thread safe 10 threads can go update that you'll always get 10 increments if 10 threads updated or not。
Yes。 Ah good question the question was hey is there ever a time you don't want to use the atomic variable most of the time you don't because most of the time you're not using threads right and the atomic operation is slower。
Alright because it's got to do other some other things in there and it actually needs the hardware to support it so it's a different instruction and there's other hardware support which means it takes a little longer。
And if you know anything about C and C++ the bottom line is let the user do things as fast as necessary but give them the tools to be able to do it correctly if that has to be an issue。
So this is one of those ones where only use an atomic number you're going to need it right otherwise use the faster ones because most of the time you don't。
Very good question。 Yes。 If you just had a regular unsigned into you and you did plus plus it's going to be faster than plus plusing this one because this one has similar。
Oh right right well sure you know the last time we could have used a we could maybe we could have done this with an autonomous number semaphore we could have done it so。
Maybe you would have used a conditional variable。 In this case we're going to see where it gets incremented or documented and then you'll go oh okay it makes sense that if we have this atomic thing we might as well use it。
We'll get there。 Ask the question when we see it in action when you see somebody doing something the next place in line。
Good so that's the that's the the couple of the structs that we're going to have again global struct。
Okay and that's it。 Now the other one again at this point it's a global it may be that multiple types of multiple threads are actually accessing at the same time。
It turns out it's okay but the cash here is still going to take the customers in order based on this array here when they end up in the array。
Okay and and maybe you'll understand that once you if you can update this next place in line atomically then you can use that as the actual as the actual index into the array and make sure that each customer gets their own spot in the array。
You'll see that see that happening as well。 Okay the waiting customer semaphore informs the cashier their customers waiting so cashier is going to go start up and then go I'm waiting for customers to finish and then just wait and then eventually a customer will signal the cashier。
Hey I'm ready to get checked out let me check out。
Okay there are other ways of doing this this just happens to be one of them。 Okay。
All right let's move on to the next thing。 All right here's the first real like major function here this is the customer function the customer function is on page。
Yeah page nine of nine right near the end right before main customer line。
Okay all right so what is the customer have to do well the customer goes in and figures out the customer already knows by the time they get here how many cones they want we will do that we'll initialize that in main as it turns out。
And the customer will then create a vector of clerks。
Okay it could have created an array of clerks of the number it's going to do doesn't matter in this case we're just making a vector we're in C++ we can do vectors。
Okay and then it is going to call thread on the clerk function with its variable i which is just the it's not really the id in this case the id will be passed in based on because we've got two things going on here we've got the number of the cone which is i and the id of the customer as well。
Okay that's going to get passed into the clerk so the clerk needs to know who to go back to anyway and so the customer is going to ask a trigger for hey my idea is such and such i want i am this is calling whatever。
All right and then and then the customer goes and brows is which just is some time takes up some time。
Okay and then the customer has to do what the customer has to wait before the customer checks out all of the customers cones need to be made。
Fair enough which means that you're going to they're going to actually join on all of their clerks and they're going to wait around until the clerks are done making their cones。
Now they might still be browsing and all the clerks end but either way it's going to at this by the time you get passed this line on line eight here you are going to the customer knows that all of the cones have been made。
Question。 Yes good question the the join here simply blocks until the threads are done and that's the whole point of join and it cleans up the thread because they're done as well。
Okay so at this point we know going forward we can the customer can check out because all of the clerks have finished their cone making。
Okay now there's no like hey here's your cone business I mean it's all that would all have to happen some other we didn't make that into this thing but you can assume that if we were really doing this and there's something else here we would actually have a cone handover sort of thing。
But in this case clerks just know or the customer knows I have my three cones now maybe they're waiting at the cashier who knows。
All right then the then the customer has to go and actually find the next place in line now take a look at this line this is where your question might come in。
Check out next place in line plus plus okay this is going to assign whatever next place in line is to this customer and then atomically increment that variable。
Okay what this means is that if two threads are coming in at this if two customers are coming in at the same time boom one of them will get the next place in line。
Another one will get the following place in line guaranteed。
Okay there is no race condition here specifically because we used an atomic variable there otherwise we could have a new text on there and then lock it and then update the place in line and then unlock it。
This makes it a little easier if we've got this atomic integer we might as well use it。
This kind of negates the necessity of a lock in this case。
Yeah yeah good question so one of the cases where you do need a music there aren't that many atomic operations increment happens to be one where we can or integer that gets increment and decrement it happens to be one where we can change it atomically。
I think you could also add something to it or subtract to multiply whatever whatever you want you do map on that one and it's going to do it atomically but there if it was a map。
There's no atomic map necessarily so still have a lock and unlock just makes it a little simpler but you simply certainly could do this with a lock and unlock you want to。
Yes。 Is it something that we can't know how is it going to be something or something like this?
Do you want to know how it's doing it atomically? Yeah so it's a bit beyond the scope of this class as far as what's happening but there are machine instructions which when you call them will do this operation atomically and you just have to set it up such that this is a little bit more than you can。
It's not just such that this uses that like uses that operation。
It may just be honestly it may be that that class just puts a locker on it。
It might just be as easy as that I don't have it looked it up but I think in other cases it's there are machine instructions and you'll speed things along so that's why you might want to use this。
[inaudible], Yeah so the question was hey look in process we block and unblock if multiple processes can change one。
It's not really a global variable because it could be calling a signal handler because member processes don't share memory。
But in this case yes if you're using the atomic variable you don't need the lock if that's all you're really doing to it is updating the variable itself。
You don't need to worry about it because it will be done in such a way that you don't need to do the lock on it。
Just another thing to show you you can use this if it's a case where you have something like this。
Don't overuse it because it is a little slower but use it when you can if you want to or you can just lock nobody's going to take off points for that if it's if you do one of the other。
Okay so then what happens after the customer gets in line well the customer tells the checkout signals the checkout person to the cashier to actually keep going so the checkout waiting customer signal so the cashier is going I'm waiting around waiting around and going oh there's a signal let's start processing this。
The next customer and the checkout the cashier will go and look up who the next customer is based on the same variable。
Okay you'll see how that works as well when we get to the cashier。
And while the cashier is checking the customer out the customer has to wait。
Okay so the customer says check our customers place wait remember this is a semaphore per each one of those or rather a what looks like hold on it is what it is a semaphore per customer in that case。
And let's see the semaphore again it's a let's see semaphore customer it's just a single zero value semaphore just signaling doesn't need any like permits or anything like that。
Could you have done this with permits maybe like with permits the thing about permits though is that there's still a bit of a race condition there as far as who gets handled next with a permit like there might be some permit thing if you signal everybody it won't go necessarily in line。
That's going to be an issue。 Okay we need the customers based on our model to be handled in order they arrived at the cashier that's the important part。
Okay all right once the signal comes back from the cashier the customer has checked out and leaves。
Okay question there are okay this weight right here you tell me what that's weight what did the customer what did the customer just do customer just signaled the cashier。
So what's the customer have to do wait for the cashier to check them out that's what's happening here。
Okay so there's a double signal going on here the signaling the cashier to say hey can you check me out and then waiting for the cashier to signal back。
Now the cashier has to signal back in the proper order right the cashier because this signal there is no waiting associated with a signal like you signal and then you go on。
So it might be that all the customers are sitting here waiting the cashier needs to go to signal the correct next customer and we'll get to see how that works in a little bit。
Okay question。 The cashier will signal you in order cashier will signal in order we'll see how that happens。
Is it possible that a person further along the line could have their ice cream cones completed or will they not have sent the signals to the cashier。
Right so your second part so basically the question was wait if the could somebody in line not have their or have their cones made before somebody further up in line。
Remember the customers just milling around until they get all their cones back then they get in line。
Alright this isn't a case where like you go stand in line while everybody goes and gets the you know whatever it's you know your one person stand you only stand in line when you get your cones so that's the order you'll be taking it。
And now it could be certainly that two customers get their cones at the same time and then fight over who gets to be in place first。
That's fine but whoever ends up in first place gets handled by the cashier first and we'll see how that happens in an interview。
Other questions on this one。 Yes。 So just some main clear is just looks a little weird though。
So the whole check out dot customers play。 That's because you literally have an array of semaphores one for each customer or you have one semaphore that's needed to track up all the way。
No let's take a look。 There is a customer's array which is a semaphore per customer。
Right so each customer gets their own semaphore what's their rating on how do they know which which where they are in line。
They do right here they get their place in line by getting the next place in line variable which is updated per customer that comes in and then incrementing it so the next customer gets the next place in line。
That's what we're doing right there。 And the cashier is just going to go through a little bit and go you you you you and know which one to actually get next。
Right and the only way the cashier is going to know to get anybody is because the cashier gets a signal。
So that's the the signaling part there。 Okay。 Does it start in the jail a little bit how this stuff works?
Good。 Okay。 If it's if you're like oh I get it all on the board awesome。
I would love it if that was a guess。 Okay。 So great。
If that's if that's what's going through your head。
Okay。 How does the customer browse pretty straightforward。
The customer just gets a browse time and then sleeps for that amount of browse time。 All right。
Sleep for is the way threads sleep for a particular amount of time。
They cause sleep for and then the amount of time in micro millisseconds and then they do that and then the customer just said customer just killed so many seconds。
Okay。 All right。 That's that。
All right。 Let's look at the clerk function。 Okay。 Remember one。
Oh, by the way, what happened? We kind of went right over this part。
Here is where the clerk threads get started。 This is back in the in the customer function。
This is an interesting point。 We are going to see when we get to main that both the manager and the customers and the cashier are all created in main。
And then the cashier and the manager immediately go to sleep basically because they don't have anything to do yet but have their thread started。
Yes, their threads have started。 So which means that it's actually less time to wake them up。
This is going to actually follow directly into the next thing。 In fact。
not this next assignment which I'll have out by tonight at some point or the by the next assignment。
You'll learn about these things called thread pools which are ready waiting threads to go do their thing。
Does that sound like any other assignment you've seen? Farm maybe。 Right。
Farm was processes waiting to go。 They're already ready。 They're what we call spun up ready to go。
That's the same sort of thing。 We are spinning up the customers。
the manager and the and the cashier but we are not spinning up the clerks until the customer actually does it。
So it's kind of like the customer goes and like hires a clerk。
You can think of it that way which would be weird。
Now we go into the clerk and we see what happens with the clerk。 So the clerk, all right。
The clerk needs to do what? Make a cone that's perfect and if it's not perfect then it needs to make another cone until the manager says you have made a perfect cone。
So there's a little while loop in here that says while not success。
This success is a local variable。 Does not need to be locked or anything。
It's before the clerk itself and that's the way it goes。 It's kind of nice。
Every clerk has its own success variable。 No need to block on that or have any reason to lock on those。
What does the clerk do? The clerk makes a cone。 The clerk locks the available lock。
The available lock is whether or not the manager is available。 So the clerk says。
"I need to lock this manager lock。", What happens if a thread tries to lock and somebody else holds lock?
Just blocks。 All the clerks could come and this is where they're fighting over the manager。
One of the managers looking at a cone and then says the manager unlocks available and then goes or the other clerk that holds this lock unlocks it and then the manager is a free for all。
All the other clerks try to grab that manager right there。
Then once the lock is gathered then the clerk says, "Oh, I'm going to signal the manager。 Hey。
go please inspect my cone。", And then what does it do? It waits。
This looks exactly like what it did before。 The first signal then you wait because you're telling the other thread。
"Do something and I'm going to wait for you to finish it。", Good? Question, how's it?
So requested the requested and finished parts of the inspections part or both semifor?
They are both semifor and zero permits。 It's just a signaling semifor in that case。
The finished one or the requested is requesting the manager。 And by the way。
what do you think the manager is doing at this point?
If the manager is waiting for the requested signal, it is waiting。
So that's what it's doing right there。 And then the manager has to then signal back to the thread。
"Oh, now you can go again。", So there's only one requested and one finished。
What does that mean again about how many things can be dealing with this struct at one time?
It turns out there's two that the manager and the clerk。
but no other clerks because they're all going to be stuck on this lock before they could go and change anything。
That's why there's only one need for a zero one there。 >> So there's like the following one way。
There could be always one waiting and always one like, "Yeah, then the manager signals。", Maybe。
maybe not depending on how much time these things take。 The manager could be sleeping。
which means the manager is going to just wait until it gets a signal。
Or it could happen immediately。 I mean, here's the nice thing about all of this。
The way we set this up, as long as things are progressing okay, it's as efficient as it can be。
There may be some wait time, but that wait time is out of our control。
We're going to make it as efficient as possible, so there's no extra wait time。
And there's certainly no busy waiting here。 We're not spinning, we're not doing any process。
We're just kind of going, "Look, I know I'm waiting for some other thread。
I'm just going to sleep until it happens。 The incident happens, I move on。", That's where it goes。
Why would we go after requested and finished? How does one form but it's waiting to signal requested and then wait on request?
Yeah, very good question。 Why couldn't we signal on requested and? And wait on requested。
I was thinking about this。 I think there's a race condition there。
If you signal and then try to wait, you might actually be the one getting your own signal。
If everything has to happen in an order where you don't, you, in an order。
it could happen in an order where you send a signal, go to sleep and wait。
And then by the time that signal propagates through the operation。
it comes back to you instead of the other thing waiting。 And not only that, there's。
the semaphore would drop down again and then it would take two signals, actually。
So that's probably the even better one。 It would take two signals to actually。
to make one of them go unless it happened to be exactly ordered。
So you want to just avoid that kind of ordering nightmare。 In this case。
just use two because you know that one thread is going to wait on the request。
and the other is going to wait on finished and you're okay。 You can try it。 In fact。
go try to build it with one and see if it, if you can get it to do a deadlock or not。
But I imagine you might be able to。 Good questions。 All right。 Anything else on the clerk?
What the clerk's doing? Oh, by the way, the clerk does what? Here, the clerk says。
waits for the manager to come back。 And then the clerk checks success and says, oh。
either it passed or not, right? And if it didn't pass, well, first of all, it unlocks。
And if it didn't pass, well, it makes another cone, right? Otherwise。
it would leave if it didn't do that。 And so it may stay in this loop as long as it keeps making bad cones。
It's just going to stay in that loop。 Really understand what's going on there。 All right。 Good。
All right。
Make a cone pretty straightforward。 It's just going to wait again。 It's going to, like, say。
I'm about to make a cone。 And it's going to get some time and then sleep for that amount of time。
And then tell how much time it was。 I'm going to run this program at the end。
It scrolls through the screen and it's like, oh, my gosh, what's going on?
But you'll see when it happens。
OK? All right。 Let's look at the manager function。 So the manager, remember。
the manager starts out knowing how many cones it's going to make that day。 It's a little weird。
We could have done something else where there's a Boolean flag that says, all cones are going to go。
All the other signals are going to be handled。 Or not even signal。
Just you could have another signal if you wanted to。
Or you could just say the signal would probably work pretty well。
Have another sum up for being done for the manager。 It says, go home。 You're done。
And maybe you could link it together with one for the cashier as well or something like that。
Although the manager can actually go home, although not the best business process。
before the cashier does。 Because cashier might still be taking care of all the other customers。
But that's what it does。 So it needs to-- so it knows how many cones it needs to make。
manager knows how many cones they need to make。 And then they are going to attempt a bunch。
We're just doing this to log it, as it turns out。 And then they're going to approve a bunch of cones as well。
Until they approve the total number of cones they need to, they can't leave。 They are going to。
first things first, well, find out if they can leave, which they can't immediately。
And then they're going to wait on requested。 Because they're waiting for a clerk to come and hand them an ice cream cone and say。
please investigate there。 Please inspect this。 Then they inspect the cone。 Just again。
it's just going to be some time。 And it's going to be some time。
And it's going to actually update the struct to say whether or not the cone passed。
And then after it inspects it, it's going to send a signal back to the waiting clerk to say。
go check your cone。 I just inspected it。 And then it does the num cones attempted plus plus。
And if inspection passed it's going to say the number of proof。 Now, the, can the。
this is going to happen at the same time as possibly the clerk is looking at inspection passes。
Is that okay if they both look at the same variable at the same time? That's actually okay。
As long as no thread is able to update while the other thread is looking at it。
many threads can look at that one variable at the same time because it's not going to change。
So that's perfectly fine。 They might do it in some weird order in with the assembly language。
but it doesn't matter。 Doesn't matter。 All right。 So what do they do after that? So what's the。
what's the manager do after they inspect a cone? They check number code。
they update number of cones proof possibly。 And then they go back in the while loop。
If they have reached the number of cones they need exit the while loop, go home for the day。
That's that question。 [ Inaudible ], Say again。 [ Inaudible ], Oh, such a good question。 Okay。 Yes。
So you're saying look, the lock happening, oops, the lock happening here。 Is this the one here?
Yeah。 So the lock happening right here。 Okay。 You just have to remember that a lock has no care about what data structures there are。
It doesn't know。 It's not, it's not saying you can't touch this data structure。
It's saying anyone else who tries to get this lock is going to be denied。 Right。
That's all it's doing。 It's not packaging up a data structure。 It's saying nobody else can touch it。
It's just saying hey, if you're going to try to get my lock you're not going to be able, to。
So it's very abstract in that case。 Right。 It's just saying nobody goes past this line until the lock is unlocked。
That's the end of the story there。 Okay。 All right。
Good question。 Does that answer it for you there? Doesn't it doesn't affect the actual。
so it doesn't, the manager can go and do whatever it。
does with that data structure at that point because it doesn't need to lock。 Now if it was。
if two threads were trying to update that data structure then you would。
need to lock and then only one would be able to do it based on your logic around locking。
So it might go a whole block if the tries to modify it or it can go? No。 You will get a note。
If it tried to, if, very good question。 If the manager tried to update something here。
in fact it does, it actually updates the, it actually updates the, in Inspector Cohen。
we'll see that in a minute。 It updates the Boolean about whether or not it passed or not。
Perfectly able to do that。 The child or the, not the child。
the clerk is not going to look at that until after it gets, a signal, which, that case will be fine。
Everything will be updated and it will be fine。 Good。 All right。 Other questions on this?
Sounds like you guys are starting to get this。 Great。 Okay。 All right。 Let's now, let's see。
Why can there only be one waiting clerk? Because of that lock。 That's the whole point of this。
that lock before。 All of the clerks would get there and go, "I can't do anything yet。", Okay。
So let's look at the, this is the Inspector Cohen。
This is not too interesting except so basically sleeps for a while while it's inspecting the。
system and then updating inspection passed based on whatever the random number that comes。
back from get inspection outcome is and then it reports on whether it's approved or not。
and then ends。 That's all the inspect Cohen does。 But again。
it does update the inspection passed very, uh, struct but that's perfectly fine, because no。
we know logically that no other threat is even looking at that right now。 Okay。
What it can't do by the way is you can't go to the customer, they can customer can't。
what if the clerk went and said, "Oh, I didn't get this to pass。
I'm going to give it back to the customer anyway even though it's not a good inspection。"。
It's not like the customer can go check this because the customer should not have access。
to this because it's not, it's just between the, the manager and the clerk。
I might be stressing the analogy of it but that's the way that, that you wouldn't want the。
you wouldn't want the customer to have access to this because this number is going to change。
for every inspection passed is going to change for every Cohen that comes through。
And it's only one value and it's not like it's stored anywhere except after the。
or except while the customer and the manager, or the clerk and the manager care。
then it's updated again for the next one that comes through。 Single struct in this case。 Okay。
Why are there no locks needed here? Because we already locked what we need to。
We logically know the only update is going to happen from the, uh, right here and the, and the。
and the clerk is not reading this value at all right now。
We know that based on our logic and that's sometimes the hardest thing to remember to。
solve to figure out。 Other questions to this one?
Okay。 All right。 Now we're finally to the cashier。 Okay。 So again。
the cashier knows how many customers there will be during the day。 Um, a little weird。 Uh。
in that sense, we could have again done something where we had a queue that kept。
or this vector or a queue, probably a queue of, um, that eventually the cashier would, uh。
get a signal that says there's no more people that are going to enter the queue。
And then when the queue is empty, the cashier can go home。 We didn't do it quite this way。 Remember。
it's a final exam problem。 It's not like this was an assignment problem。 But, uh。
but that's that the cashier。 Now this is where it starts to get interesting for the cashier。 Okay。
The cashier does go in order。 Right。 The cashier goes from zero to the number of customers。 Okay。
And the cost, the first thing the cashier does is wait on the, uh, the waiting customer。
sell for it。 In other words, if nobody's there yet, don't do anything but just sit there and wait。
Okay。 Wait for that thing。 Could you have maybe done it where they wait on the first one and then the signal comes in。
maybe。 That might have been another, another way of doing it。 But in this case, we just have that。
some affords is waiting on several。 Then what does it do? Okay。
It rings up the customer eye because anytime it's, when it gets that first signal, it knows。
that signal had to have the first one anyway, has to come from that first, uh, from that, first, uh。
customer。 Okay。 And so it brings up the customer。 And then what does it do? Well。
it knows that that was the customer just rang up。 So it signals that particular customer through that customer semaphore to say you're not。
We have checked you out, go at your ice cream。 Okay。 And then it goes and does the next one。 Now。
what could happen in the meantime if another customer has come in and is waiting, in the queue。
well, this weight right here, this weight right here will blast right through。
It'll blast right through that because another customer has already signaled。 And this is different。
This is a difference between semaphores and signaling in, um, like a, uh, signaling in, processes。
The signal remember does what? It's on a semaphore。 It just decrements or increments a counter。
So there's some counter there that has been, uh, in the, in case of the signaling has incremented。
that counter。 And so by the time this weight happens, if another customer is already there, right。
through and handled the next customer immediately。 Okay。 That's an important part right there。 Okay。
And then, uh, it signals that customer goes to the next customer, might have to wait if。
the customer's not ready yet。 Uh, if the customer is ready, then, um, then it will just go, the。
this will go right up, and ring up with that customer。 But it's doing it in order。 And that's the。
that's the important part here because this is kind of a cued up sort of, system。 Okay。
Once all it goes through all the customers it needs to goes home。 Okay。 Question。 Yeah。
Good question。 How is it that it's doing it in order of the customer? It's not only the order。
sorry, it's not when they arrive。 It's when they arrive to check out。 In other words。
the customer has gotten her ice cream cones and then she goes up to the。
cashier or goes up in the line, which may have other customers in it and stands there。
At that point, let's go back quickly and look at what the customer, uh, what the, let's。
see, this is what the customer was doing。
Here's where the customer is doing this。 Remember。
the customer gets their place in line by checking the next place in line variable。
and then that's the, that's the signal it will end up waiting for。 Right? Because it actually。
remember, here's where it does it。 It waits in its place in line。
And so the only way it can get in line is to go and get the next place in line and then。
update the counter so the next person line gets the next place。
It's kind of like taking that little bar at the supermarket and moving it behind your, groceries。
like, you know, the next person is going to be behind that, right? And that's what you're doing。
You're like, oh, here's my place and then I'm going to put the bar behind the next one。
That's kind of what's going on with that, that update right here。 Okay。 Other questions on that?
Good。
Let's go back and look at, let's see, we did the manager respect on cashier。 Okay。 Cashier。
cashier is done when they check every out in order。 Yeah。 Okay。 You got it。 What was it?
Is this something interesting? No, this is exactly what she was talking about waiting for。 Okay。
How does it know to wait in order? Yeah。 The customer gets the ordering。
Because if we're updating the customers' array with their order and then we're just looking。
for that array in signal。 Yeah。 So it is, it is, yeah, this is, I mean。
this is an interesting thing。 Way of doing this。 This function has no real idea how many, like。
which customer is where, except for the fact, that it's going through this loop。
And the only reason it gets through the loop is because it first has to wait for that semaphore。
for the waiting semaphore, which is, are there any customers? Is really what it is。
And then as a customer comes through, the customer, when that signal happens, that gets documented。
again。 But if another customer comes in and gets incremented, and so if two customers come in。
it will be, high enough that you know there's two people in line。
And then that will go right through。 But it goes in order because of this for loop。
forces it to go 0, 1, 2。 And which one is it actually signaling? It's signaling 0, 1, and then 2。
based on that。 Yeah。 And remember, this program, there is actually very, very, very simple。
kind of thing。 And there is actually very little actual communication between the threads。 I mean。
it's really only the customer is just waiting for the signal。
And then that signal comes out of nowhere and it moves on。 You know。
it doesn't know what place -- it knows what place in line it is。
But it doesn't really care at that point。 It's just going, "I'm just waiting。"。
And then it gets a signal that's, "I'm done。 I can leave this store。", So, yeah。 Good question。
Okay。 Could we have handled the cashier, customer cashier if we handled the clerks manager without the array?
Not really, right? The problem is that we needed to do this in order。
So we need to have enough semaphores for each one to wait in their place。 Otherwise。
if there was one semaphore, it would be -- there would be fighting going on when that signal came through。
And the person who came in last in line could all of a sudden be jumping up in front。
That wouldn't be so good。 We wouldn't like that at all。
Kind of like that one where a line of his grocery store ends up。
Somebody opens it up and the person in the back of the line goes straight to the other cashier。
And you're everybody else like, "Well, that's not the right order。", Chaos。 That's it。 All right。
Any other questions on what the cashier is doing? Okay。 So, we have -- so, yeah。
we made -- we needed to do that。
The main function -- we finally made it to the main function here。 So。
the main function sets things up。 And let's see how it actually sets things up。 Okay。
It sets up the customers, the manager, and the cashier。 Remember。
it does not do anything with the clerks because the clerks happen in the customer --。
the customer somehow magically creates a clerk thread。 Okay。 That's how that works。 Okay。 So。
the customers -- the total cones order -- this is just for logging, basically。 It sets up here。
And then the customers -- we know how many customers are going to come in because we're。
writing this program。 Maybe that could be some other random number。
but that's the -- that's -- we did it as a constant。 Okay。 For each customer, what do we do?
We get how many cones they want。 That's where we determine how many cones each customer wants。
Then we set up a thread for each customer with the customer ID, basically, and with the number。
of cones they want。 And that's how each customer knows to go and get so many clerks -- one for each cone -- and。
then it passes on its own ID to the clerks and to the cashier。
And then we actually just keep track of the total cones order because we want to report, on that。
All right。 We're going to have to join these at some point because they're all threads and they。
need to -- when they end we need to join them, we'll get there。 Okay。
Then we need one thread for the manager and one thread for the cashier。
And we don't need to tell them anything。 At this point -- well。
we need to tell the manager -- the total number of cones ordered。
and the cashier will use the global constlet to tell how many customers there are。
So it doesn't need to be passed in。 Probably should have been in terms of the encapsulation question。
So notice that -- I noticed that the cashier gets joined before the manager。 What's the idea there?
Is that on purpose? Cashier gets joined for the manager。 I don't think it matters。 Yeah。
I don't think these are irrelevant because they're both ending and they're ending and。
they're not coordinating and they don't really need to worry about it。 Yep。 [ Inaudible ]。
The max number of clerks is the total -- total cones ordered, yes。 Yeah。
That's because we know that each clerk only makes one cone。 And no, that's not true。
Each clerk could make infinite number of cones, but it only makes one good cone。
It only makes one passed inspection cone。 So the other question。
Is it possible that the clerk signals before the manager is fun?
Is it possible that the clerk signals before the manager is fun? Sure。
Nothing happens then because nobody is -- that -- all that's happening when you signal。 Remember。
this is not the same as signaling in a process。 This is just updating that semaphore。
And so the semaphore is going to get updated no matter what。
Who cares if there's a manager thread yet? Unless the manager thread gets ready and checks that signal。
does it wait on that signal, it will move right on。 Yeah。 Good question。
Like this is not signaling in the same sense。 It's too bad it's called signal in some sense because it's not really the same like I'm。
signaling。 You're signaling the semaphore and then somebody else might be waiting on that semaphore。
And so that's where the abstract signaling part comes in。 Good question。 Everybody else on this one?
So then what do we do after we -- so now here's the most interesting part。
And I already mentioned this a little bit。 The manager thread -- so the customers are just all spinning up and going about their。
business。 They're creating clerks and so forth。 The manager and the cashier。
the first thing they are likely to do is just wait。
But they are already running and ready to go and they're just waiting for that one signal。
to happen and through the condition variable any and when that happens, boom, they go and。
they start respecting。 You don't have to spin up a thread and spinning up a thread takes a little time。
This is a good way。 This is going to set us up for that thread pool stuff we'll talk about as in two assignments。
from now。 We'll get to there。 And thread pools are a good way to make it so that you reduce the lag that comes with。
creating a thread。 There is some。 Okay。 Then what do we do? We join all of the customers。
Then we join the clerk and we join the manager。 I don't believe that matters what order those happen in because we're just waiting for them。
all to end。 That will mean that we don't end our program until that finishes。 All right。
There's a lot of code to look through。 Hopefully we did it in pieces so you got the feeling for whichever。
what they all did。 It was much better than me typing it all live。 That would have been a nightmare。
Any questions on the code so far?
What are the takeaways from this? There's a lot going on here。 We made a big model。
Not even that big really, but it's a big enough model that there's lots of moving parts managing。
all the threads, waiting。 You do have to plan this out。
This is not something you can just throw together and go, "Oh, now I need something。"。
You had to sit there and plant。 Trust me, Julie Zelensky, once you created this。
must have spent some time going, "Okay, what do I really need here?
I want all these things to happen。 I need some managers out there going to need a semaphore。
There's going to be another semaphore for the clerk and all I need to and whatever。
It's not like she just started writing code。 She's certainly planned this out to some extent。
Although I guess I know Julie enough to know she probably just did started, but she's good。
enough to do that。 I'm not going to do that。 Anyway, you didn't even plan these things out。
This is not the only way to do it。 You could modify this model in a zillion different ways。
You could make it so that the clerks, there's multiple clerks that all are always spun up。
and then you get one in time。 That'd be a thread tool sort of thing where a clerk。
once they make a good ice cream goes, back in the pool and gets another one。
Maybe you'd want a semaphore, some number of permits with the number of clerks that can。
do it at a time or something。 Who knows? But you would do that。 If we had more than one manager。
we've already talked about the details of that。 That's going to not involve more structure。
different structure, semaphores。 Then you could have done a main and then spun them up a little bit。
Yeah。 So I keep throwing around this thread pool thing。
A thread pool is basically a number of threads that are all waiting to do a job。
I think it's exactly like the farm where you have all those Python processes that are。
still running on machines around the world。 You've got all those processes that are all ready to go。
waiting for something to do and, then boom, they do it。 They don't need to start anything else up。
You will see that into assignments。 You'll actually build a thread pool and see how it works。
But it's not really that complicated。 You have a whole bunch of threads and then you say go but just do a wait and then I'll。
eventually signal you。 It's really all there is to it。 And then, yeah, that's the thread pool。
waiting around。 We want to avoid spinning up a thread, taking the time to do that if we can。
But for this program, we did it for the clerks or we didn't do it for the clerks。
We did it for the manager and the cashier。 All right。 So that is the program。
Let me actually show it to you in action。 Let's go find a terminal here。 Oops。 Skip this version。
Need to go back to the cursor。 Skip this version。 Hang on。 There we go。 Okay。 All right。
So we need to。 Let's see。 Are we in myth? Yes, we are。 We need to go to 110 and spring。
Live lecture thread。 CPP。
Okay。 In here, I've already created the ice cream parlor。 Let's see it go。 Now。
this is going to go on。 It goes on and on and on and on。
If we kind of go up and see what's going on here, clerks starts to make ice cream cone。
zero for customer number seven。 Managers presented with an ice cream cone。 Clerk just spent 0。
287 seconds making an ice cream cone。 Right? Et cetera, et cetera。 And it keeps going, keeps going。
keeps going, keeps going。 And you can kind of see, eventually it's going to keep going。
I don't know how many there is。 Okay。 So at the end here, what happens, well。
it says the managers presented with an ice cream, cone。 And then the manager is done。
Manager inspected a total of 333 ice cream cones before approving a total of 27。 Terrible clerks。
it turns out。 Right? Wasting a whole bunch of ice cream。 90% of the ice cream is wasted。 Not good。
Not going to be in business very long。 And then the manager leaves。 Right? And then the customer。
the last customer here takes up position, let's see, 14 at the counter。 Why would that happen?
How do we have customer tenting? I don't know。 I'd have to look into that。 Why it's not position 10。
I'll flip it up。 I'll have to see。 Oh, I know why。 Because 12, 11, 5, whatever。
they all happened before。 Turns out that it took the most time to make customer 10s。
which is not the order of customer, 10 gets in line。 The order of customer 10 was the 14th cut。
the final customer to get their cones made, is what happened there。 Okay?
And then the cashier has already run everybody up。 It took less time to ring them up。
I'm going to cashier good one。 Yeah。 [ Inaudible ], Close。
The question was technically the cashier that the manager thread ends before the cashier。
The manager thread is joined after the cashier thread。 So if you want to think about it。
the cashier has to wave at the door before leaving。 Yes。 Although the thread is actually gone。
but all the cleanup for the thread has not happened, yet。 So I'm going to say the cashier。
the manager left their coffee cup on the table。 Somebody has to go clean it up。 That's still there。
But the manager is long gone。 All done。 Okay。 All right。 Feel free to look at this code, modify it。
check it out, try to make some other semaphore, changes, see what happens。
This is not a bad problem for our final exam。 If you want it, if this is the kind of -- now。
coming up with Hall, making it work is the, hard part, like for designing the problem。
so it's unambiguous。 But this is a pretty good problem。
So I would really try to understand this if you go back to the slides, look at the code。
run the code, and you may see something similar on the final exam。
We'll see you guys through the results。