斯坦福-CS110-计算机系统原理笔记-二-

89 阅读1小时+

斯坦福 CS110 计算机系统原理笔记(二)

P11:Lecture 10 From C threads to C++ threads - main - BV1ED4y1R7RJ

Welcome。 All right。 Welcome to the 19 of you that are here。 No。

it's more than that for people watching this on, video。 There's more people than that here。

So assignment three, hopefully ended up okay。 There were lots of。

Piazza questions and lots of consternation about race conditions and this and that and that's the kind of thing。

you have to deal with when you're doing multi-processing, also multi-threading。

which we're going to talk about, today and you will do more multi-processing for this week's assignment and then of course you'll practice it a。

little bit on the mid-term as well。 So big week this week。

I think I'm already behind but we will keep going, forward here today。 The mid-term is Thursday。

It's 6 to 8 p。m。 Hewlett 200 which is across the hall。 It's not this, one。

Don't sit in here and think where's everybody? It's across the hall。

If you are taking an alternate exam, hopefully you already emailed me。 If you haven't。

please do that today so I can set things up and same thing for, any accommodations that you have。

Do that。 I plan on sending out emails about that this evening for people who。

talk to me about those things already。 The exam is going to be on Blue Book。

Who has used Blue Book before? Raise, your hands。 About half of you maybe。 To the other half。

what it is is you have your laptop and you load a program on it。

that you take the test on and it submits it to us and we grade it all kind of digitally。

The nice thing about Blue Book is, that you get to type your answers。

Some people think that's nice anyway but it feels very much like taking a paper。

exam in that we don't really, you can't compile code, you can't look things up。

You can't copy and paste within the, application and so forth but you should get it working on your computer before coming to the midterm。

What happens is, we will publish an encrypted exam sometime on either Wednesday night or Thursday morning that you can download and then。

when you get to the exam we give you a password and then it starts the exam for you and it is timed on the computer。

If for some reason you don't have a laptop or your laptop battery is going to die after five minutes or something。

you should talk to me, sooner than later so we can find you another laptop to use or for your battery issues we will have some plugs but they are not going to be。

like for everybody。 Most people's laptops these days will last two hours although I know some people are。

there will last like an hour and a half and you have to, get near a power plug。

We will have that available for some extent。 You are allowed one back in front piece of paper for your own notes or whatever。

Please don't, bring any magnifying devices regular glasses are fine like I don't see people like you know like Microscope here。

I think I told this to people in 106D when I was doing a similar sort of thing。

I once heard and I mentioned, somebody else recently I once heard of a professor saying you can use anything you can bring into the exam for notes or to use for the exam and somebody came in carrying a graduate student。

Popped the next to him and that was that。 No, you are allowed one piece of paper back in front。

We will also, did that, did the joke just finally sick。

We will also give you a somewhat limited resource resources sheet。

It looks kind of like this where basically gives you all of the。

prototypes and things for functions that you'll need and then some some specific constants and things that you'll need。

We are not trying to make you memorize those sorts of things。

So in the middle of the exam you're like I forget how to do that。 It's probably not going to matter。

We're not going to try to compile your code necessarily。

But but either way it's not that we're not testing you on that。

Do the best you can with those sorts of things。 Let's see。 I think that's about it。

All the comments I have at the mid term。 Any questions at this point?

About the mid term comments。 It's two hour exam。 I'm going to try to make it so that you finish it that most people finish within an hour and a half or so。

I mean I don't want this to be a test of your ability to like race through hard problems because they are sometimes difficult。

Awesome。 Good question。 What's the format going to be? Is it going to be like the practice ones?

I would say it's going to be kind of most like the last practice one which is a couple of programming problems。

I'll try to walk you through the details of that pretty。

I don't want a wall of text if I can help it。 And then maybe a problem like the try to figure out what's going on with these signals like what's the output sort of thing。

And then something else is going to be a little bit more difficult。

And then some short answer questions which kind of cover the spectrum。

That's probably the way it'll be。 I'm still in the middle of finishing it。 Anybody else? Okay。

So we have another assignment。 This assignment, Stanford Shell。

I'm going to do a little demo of it to show you what it is。 Is kind of a neat assignment。

It's basically building the shell that you use every day and have used since 107。

And it's it needs to handle certain things。 You need to be able to do the basic things like quit it or exit it。

You will be putting processes in the background or in the foreground kind of like what we did in our simple shell。

So you can use that as a hey, how do we do it then and what do we need to do differently now。

You also have to keep track of all the processes。 So you'll be able to have multiple processes。

You can throw in the background and then maybe a program process as well。

And your program has to handle all of those。 Okay。

So you need to be able to use FG to bring a background process into the foreground。

You need to be able to do BG, which restarts a stopped background process。

And you can tell it which one as well。 You also have to be able to kill a set of processes。

And this is an interesting one。 We have this term, this command called slay。

which basically will send to a process group。 And that's a group of processes like for instance down here。

We have LS pipe through grep pipe through cut。 Well, they will all be in the same process group。

But if you kill the process group, they all die。 So that's the sort of thing you're going to have to think about when you're doing this。

You also can halt a process that's currently running。

You're going to send a SIG stop to it or actually a SIG, actually, I think it is a SIG stop to it。

You will be able to continue a process。 You'll also be able to tell what jobs are running with a command so you can see what the process IDs for all those jobs are。

And you're going to be responsible for setting up the pipelines。

I think this is one of the trickier parts of this assignment。

It's getting this to work where you have LS pipe through grep pipe through cut。

For any number of these。 And you'll also have to do things like sort input from a file as standard in error I get read from standard in。

And then maybe output to another file。 So those are things you're going to have to think about as well。

So there's a lot of moving parts to this。 For that, the piping。

you have to think about what's happening here and you have to go, okay, well。

LS needs to have its standard out going to grep as its standard in。 It sounds a lot like pipeline。

right, which you've already done, but this is a more robust version。 And then same thing for us。

grep has to send its output to cut。 So guess what。

there's going to be like a pipe here and another pipe here。

And they're not going to be the same pipe and you have to start them and stop them in different places。

And so it's a little bit trickier to get that going。

You'll also have to be hand off terminal control to foreground process。

Sometimes when you type in process, well, your terminal isn't really grabbing the input anymore。

It's whatever program is running。 So there's a little bit of a hand off there。

That's not actually one of the hardest things about the program。

You just have to end up thinking about it。 Question。

So it's going to rely on pipeline and sub-process from the last time。 >> It is a good question。

It's not going to rely on pipeline sub-process。 You kind of recreate them in the form that we。

at least the pipeline one in the form that we will have for you。 Yeah。

What are their questions just so far on this? All right。 This evening。

I will do my best to put up a little video describing some of the details a little bit better。

There are a lot of moving parts to this, okay。 There's a ton of moving parts because you have to kind of move into the form。

You have to think about all the nuances to this shell。

There's a bunch of header files always read through those first。

If you've already downloaded the assignment, read through those。

You're only going to be putting modifying one program。 So that's at least like good。

It won't be like trying to figure out six different programs。

You can test the shell either by running programs。 I'll show you in a second。

Or through a driver program, which basically runs the shell using some input so you can see the output in a way that you can test a little better。

So it's a good way to test。 So get used to using the little driver。

You would definitely have to have a SIG child handler because all of your children are going to be starting。

continuing, stopping, all that kind of stuff。 And it's the SIG child handler that's going to be manipulating those and taking care of which programs are stopping when and so forth。

In some sense, it's a little like farm in that you have to get your SIG child handler pretty robust to get these。

You'll also have another handler or maybe two。 And by the way, you can make these into one handler。

If you want your shell, if I go to a shell, by the way。

if I go over to a shell and I type control C, my shell doesn't die。

You're going to have your shell also make it so that it doesn't die when you do control C。 Well。

that's actually where to do that you have to handle and you have to capture SIG in to which is the interrupt and SIG stop。

which is control Z。 And they should affect your processes that you have, but not your shell itself。

So there's a little bit of that going on as well。 As I said。

the piping between processes is a little bit tricky。

You'll have to take some time to get through that。

This assignment has lots of a whole list of milestones。 You should probably do this, then this。

then this, then this。 If you can follow those milestones。

you'll be able to do some testing and it will be a little more straightforward。

Don't try to jump to the last part or like just go down some route。

Test as often as you can is the big thing。 Now, this is a detailed assignment。

We also have a midterm coming up this week。 It's due next Wednesday, which is a week and a half。

This should be about a week long assignment。 So I'm giving you a little bit of buffer room in there。

If you wait until Friday to start it, you can get it done。

but it's going to be a lot of work over the weekend and over the beginning of next week。

So I would at least start the assignment。 And by the way。

it's got all the signal handling stuff you will need to know for the exam。

We will not ask specific questions, although the practice midterms did on Stanford Shell。

but we will, the material is ostensibly the same。 So you'll, starting to do the assignment。

at least you'll start, you'll continue to think about those。 Okay。 All right。

And then we'll see how it goes as far as how people are doing after beginning of next week。

All right。 What questions on the assignment at this point? Anything else? This is a fun one too。

It's definitely got a lot of parts。 So I said, okay。 All right。 Then let's move on back to threads。

Now, remember, we have left multi processing world and we are now in multi threading world。

The biggest difference is that threads are all under one single process。

which means they share their memory。 They have a different stack space for each thread。

but they can relate to each other's stack frames。 You can pass references to global variables。

That works just fine。 And you can have threads interacting with, like, say。

one global data structure very easily。 That's one of the benefits of threads。

They're considered lightweight processes in the sense that they really are doing different things。

You can actually set the affinity on threads to run on different cores。

And they really do run in parallel, especially if you have different cores。

So there are benefits using threading, but there's a lot of little things you have to think about。

just like multi processing。 And there are many analogous things to multi processing that you'll have to look at。

On Wednesday, what we did was we looked at the C version of P threads。 Now。

threading doesn't come standard with Unix。 It's kind of a test。 It's kind of, well。

it's more or less added these days, but it's another library that we're going to use。

It's another library that you have to run。 So that's why we had P threads。 And for C, of course。

you have to do all these things with void star and pointers to send various blocks of data。

And it's kind of annoying。 It turns out in C++11, which is the version that we are using。 In C++11。

we have actual support for threading built into the language。

And it works a little bit better and is actually much cleaner once you get to understand it than P threads are。

So we're going to quickly transition into that and then never speak of P threads again。

Although if you take a networks class or maybe an operating systems class。

you're going to have to probably implement those and think about those in the future。 Yeah。

Is threading in the P threads on the term? >> No, no。 There's no threading in the mid term。 No。

good question。 Yeah。 Unless you want me to put it on there。 [LAUGHTER], So anyway。

let's take a look at -- we'll go back and look at the recharge or the introverts program that we had before。

Okay。 So let's actually log on to myth here。 Oh, I forgot to show you。

I'm going to show you Stanford shell。 I forgot about that。 [INAUDIBLE], There we go。

So Stanford shell looks like this。 You can do things like LS。

You can do things like sleep in the background。 And it will -- oops。

Needs a sleep something like 10 in the background。

And then it goes in the background and you can type jobs and it will tell you that there's a sleep running。

If I quickly do another one, I'm going to have time。 Let's do sleep 20 and then jobs。

Jobs。 Then there's still only one。 Let's do another one。 Sleep。 20 in the background。

Now if we do jobs, there should be two。 Now there are two jobs in the background。

We should be able to -- I think kill -- did I say it's not kill。 So let's go back to the next slide。

So let's go back to the next slide。 So let's go back to the next slide。

So let's go back to the next slide。 So let's go back to the next slide。

So let's go back to the next slide。 So let's go back to the next slide。

So let's go back to the next slide。 So let's go back to the next slide。

So let's go back to the next slide。 So let's go back to the next slide。

So let's go back to the next slide。 And then you could put that through word count and so forth。

You could also do that and then output it to word count out。txt。

And then we could do cat word count out。txt。 It should look very much like a terminal。

And that's what you do。 I shouldn't be able to do control C。 It actually says control C。

but it doesn't kill the shell。

It shouldn't anyway。 And then control Z will not do the same thing。 If I do something like。

let's do sleep 20。

And then I do control Z now。 It should bring you back and then have a background job there。

which it does。

So there's all the different parts that you're going to have to do for that assignment。

What is the 12? The 12 means the 12th job that you've actually launched so far, I think。

in your shell。 I think that's all it means。 Never。 [ Inaudible ], Oh, well。

the question was why are we using STSH for cat? Cat actually will print out a particular file。

That's all we're doing there。 And I happen to have that in that folder so I know that file is there。

So that's why I just said cat that one。 I just happen to know that file is this。

Any other question on that? Okay, and then to exit, quit or exit。

That's Stanford shell。 You'll get used to that。

And by the way, use that solution one to see what the output should be like。

And some people sometimes miss that。 All right。 Let's see。

Cpp now。

Okay。 Let's look at a new introverts program。 This is the recharge。

Basically what we're going to do is the same thing as --, same thing as we did for the same program。

It basically has 10 threads or, I guess, six threads in this case。

And they're all going to print out something about recharging when you're alone。 Okay。 In fact。

we'll do that right now。 We are in C++ now so we can start to use C out。 Okay。 And if we do C out。

I recharge by spending time alone。 Okay。 And then -- and then N to L。

One thing about C out is unlike printf, it is actually not thread safe。

Which means that -- and it turns out here's how it's not thread safe, by the way。

Each one of these less than less thans are a new function。

So basically this can print separately from the end L。 And so we want to avoid that, actually。 Well。

you might not want to avoid that, but let's say you want to avoid that, we normally will。

If we want that entire C out statement to print without any other。

thread interrupting it -- by the way, it probably won't do it like between time and alone。

but it will do it like any time you have this。 So if we wanted to not interrupt that。

what we can do is we can actually use a function, that Jeri Caine created。

which is just a lock on it, which says, "Hey, after I say OS lock inside a C out statement or inside a C out function。

I just want to not have any other thread get in there and do something, while it's still doing that。

" And then after this, we should also have OS unlock。

And this is using a header file called OS stream lock that we'll just link you to。 Okay, question。

Chase。 [ Inaudible ], Couldn't you just use print F? Sure。

But there might be some things that you might want to see out that are easier to do。

and to see out maybe some output of some other -- I don't know。

some other type or something that has a built-in overload for out or something like that。 But yeah。

you should just probably stick to see out in this for C++。 It's also the better way to do C++。 Yeah?

[ Inaudible ], In this case, you could use a new line character。 Yes。

although new line may not flush in this case, but yeah。

you probably could use a new line in this case。 But sometimes what if we wanted to do something like this?

I'll just do this one。 Like, see out, this is -- I should do it in var a colon a。

You know what I mean? Like that, you know, period or whatever。

Then we still want to wrap it in that OS lock, OS unlock。

It's easy to use anyway。 All right, and that's that。 Somebody else had a question up here。

or maybe it was the same。 No? Okay。 So anyway, that's what we're going to do there。

In fact, that's all this function actually does。 Okay?

And then what we're going to do is we're going to use the actual, threading library built into C++。

Okay? So let's hear from K-num-introverts space introverts period。

And why didn't I use the OS lock there? We're not in a thread yet。

Like we're not even in a thread yet, so not really necessary。

And although it wouldn't break if we did use it, it's just don't use if you, don't need to。 Okay?

There's a thread library。 I'll talk about the details of the thread library after we see the example。

Okay? Here's how it works。 We're going to do the following introverts。 Okay? And K-num-introverts。

Introverts。 Okay? And that is actually setting up。

So we're calling the default constructor for this thread class。

And it is placing them into that array one after the other。 Now you would think, oh。

does that mean that it's actually launching a thread? As it turns out。

the default constructor doesn't do that。 And then I'll show you what we do in a minute。

This is actually a nuanced thing of C++11 that I'll show you。

But for now, just know that that sets up the array of threads。 And then we can do the following。

So for thread, definitely need the ampersand on there to make it a reference。

And you'd buy the way you could put that reference there too。 I like to put it here。

but some people like to put it before or with a space or whatever。

It doesn't matter as far as C++ is concerned。 Introvert in introverts。 Okay?

And then what we are going to do is we are going to do the following。

So introvert equals thread recharge。 And that's all you need to do to actually say, hey。

here's a new thread with the recharge function, launch it。 Okay?

You can have as many parameters as you want here。 If you want to send in other parameters。

we'll do that in a second。

You can do that。 But this is for the simple example。 That's all you need to do。 Now。

the interesting part is happening right here where you're doing this weird。

and if I spell the introvert right, that would be better。 You're doing the exchange as it turns out。

Instead of just setting it equal, I'll talk about that more in a minute。

But you might think to yourself, wait, didn't I just start 10 threads?

And now I'm doing a copy of 10 more threads。 And how does that work? And so forth。

It actually is because C++ allows it to work as it turns out。 Okay?

Then what we can do is we can do another same sort of thing。 In thread, introvert, in introverts。

Okay? And then we are just going to do introvert because this is C++ is a class。

Join, which does the exact same thing as join the function called it in C and it waits for that particular thread to end before it moves on to the next one in the fourth。

Okay?

That's the beginning one。 Let's see if it works。 Anybody notice any type of stuff?

Let's see。 Make, introverts。 Okay, looks so good。 Introverts。 And there we go。

I recharged by spending time alone six times。 Okay?

So let's go back and talk about the details of this。 A little more, in a little bit more detail。

Anybody have any particular questions so far on this?

So the syntax for the formants you're getting the introvert by reference? Yeah, very good question。

You're getting the introvert by reference in the line down here。 Yes。 And here。

You're getting it by reference from the actual introverts array。 And well, yeah, good question。

Why are you doing that? We need to, as it turns out, this is the C++ part。

And I guess we should just talk about that right now。

We're trying to take a thread that we're creating and plop it into an array。

Normally what would happen is you would copy all the resources out of the thread and place。

it into that array。 Right? Well, that would mean there's now kind of two threads that are going and there's no real。

way to like shut down the one and start up the one in the array。

It just happens that that doesn't work in C++ normally。 Well, C++ itself。

I think this is on the next page, has a new function, which is, here it, is。

operator equals thread two ampersands other。 It's just a syntax thing。

It basically means we are setting up this move type of equality。

Basically it's swapping the two things。 Okay? And all you need to really know about that is that this works。

And so, what's kind of the bottom line is that when you do this, when you do the actual。

equals right down here, and you say, okay, look, the thread that we are creating here is。

actually going to be placed into the place where the reference is in the array。

And it actually does a complete swap and places the non-working thread that we constructed。

in the array into our actual as the kind of thread over on the other, on the right-hand side。

of the array。 And so, what we're going to do is we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 And we're going to do this。 And we're going to do this。

And we're going to do this。 Yeah, good question。 Are there things like W-No-Hang and W-On-Trace and all that?

Not really。 You're not going to have the same models when you're doing this。 Basically。

you're most often going to join at the end。 And it's not like you're going to stop a thread using SIGSTOP and so。

forth。 It's not a process in itself。 Most of the things that we did with threads are processes in。

that sense are not going to be really in the same model of, threading。 Okay。

threading is most of the time breaking up a problem into。

many subproblems to solve kind of independently。 We'll see an example of that in a few minutes。

And that's where it differs a little bit from multi-processing。 Remember。

most of the time multi-processing is fork exec。 Fork exec fork exec。

That's most of the time what we do with processes so that we can, launch other programs。

This is now saying everything's going to be in my one program。

but I'm going to make them all kind of happen in parallel。 Yeah。 Good question。 All right。

So that's the actual program。 Let's see。 What else can we talk about here?

This is the important part。 We declare an empty thread。 The empty thread handles array。

That's the important one。 Okay。 And then we install the recharge function into temporary threads。

That's just saying thread and then the name of the function。

And then they get copied over or I should say moved into the, array。 Okay。

So that's kind of the new C++ part。 All right。 And you can read a little more details about that as you go。

Join method。 We talked about the function can be anything we want。

Normally we just have a void function because we don't actually, ever get the return value of it。

It's not you can't really get the return value in the same sense, that you can in wait PID。

But it can be the function can be anything。 So you'll see lots of functions you'll write that we actually。

launch in a thread。 And it's actually pretty nice。

It's in some ways much simpler than multiprocessing。

And you don't have to have any special types of functions。

Just call the function with the parameters you want。 There it is。

Now it's running in its own thread。 It's actually relatively clean。 Yeah。 >> Can you give arguments?

You can give arguments。 We will talk about that now。 Good question。 Okay。 All right。

So let's move on to a different example。 Okay。 This is going to be a greeter program。

And it's going to be just a program where we are going to have。

a number of threads run and print out a little greeting based on。

their thread number that we're assigning it。

So let's look at that。 Oops。 There we go。 Maybe that's the one。 I hope that's the one。 There we go。

Okay。 So in this one we are going to have a function that takes a。

parameter。 And so this is how to actually send that parameter。

You can send it by value or references。

It turns out you can do either。 We can -- we'll show you how to do both of those。 Okay。

In this case we are going to have each greeter grader。

And we're going to have a number of different types of parameters。

And we're going to have both of those。 Okay。 In this case we are going to have each greeter grader grader a。

whole number of times。 I'll show you what I mean。 Okay。 Size t -- size t。 I equals zero。

I is less than the id。 So whatever id we pass in it's going to greet them that many, times。 Why not?

It's kind of a silly program。 Okay。 So whenever we have to do osloc because we are in a thread and。

we can say greeter number and then id says hello。 And l。

And then we do osloc because that's after the entire line。 Okay。 All right。

And then what we're going to do is we're actually going to do。

something here where we pause for a little bit of time so you can。

see the interleaving of the threads。 So we're going to do kind of this interesting thing。

We're just going to use a little time structure called time, spec。 You don't have to know about it。

I mean we'll never ask you about that。 t s equals and then you can set it up like this。

It's got a few different things in random。 And then we are going to -- oops, random mod。 1, 2, 3, 4。

5, 6, 7, 8, 9。 A big number。 Okay。 And let's see。 I think that's that。 There we go。

And then we're going to do this thing called nano sleep for that。

amount of time which is a slightly small amount of time because, it's time divided by a billion。

Okay。 And then we are going to end our loop。

I don't know why this is not。 There we go。

Okay。 And then we are going to do another cout, os lock。 And we're going to say。

creader number ID as issued all of their hellos comma。

And then you can -- I guess because the next line here we'll, do it on the next line。 So let's see。

So they go home。 And L。 And then os unlock。 And that's it。 Okay。 So that's our greeting function。

Okay。 Question。 >> Is there a get-de-ed like get-de-ed sort of phone? Can you join in this function?

Is there a get-de-ed? Can you join in this function? There is a get-thread-id。

You can get your threads-id which is more or less a process, like id if you want to do that。 No。

you wouldn't be joining inside the thread。 You don't want to be joining inside the thread。

You want the main program to do the joining。 Yeah。 Maybe some nuances to that。

but in general don't try to join, another thread。 And by the way, again。

multi-processing and multi-threading don't really mix too well。

You don't want to fork inside a thread。 Otherwise lots of things could be bad。

You could do threads inside a process that you fork but not, the other way around。

Just the way it works。

You've got to be used to that。 Okay。 So anyway, that is our grid。

Let's just make sure we made that make-readers looks good。 Define and I used okay。 Good。 All right。

So then in main, what we're going to do is we're going to do, something like see out。

Welcome to gretland。 Welcome to gretland。 We're going to gretland。

We should probably capitalize that if it's a proper noun。 Okay。 And then and L。 Okay。

And then thread。 Greeters。 This should look familiar。 K-nom-greeters。 And then four。 Size t。

I equals zero。 I is less than K-nom-greeters。 Semi-colon I plus plus。

Okay。 We can do greeters。 We could have done it the way we did before, but this is just a。

slightly different way of doing without the reference。

We'll still do a copy like we did before and move like we did before。 Okay。

Greeters I equals thread and greet。 And then what we do is we just pass the parameter as the next parameter。

And that's what it does。 It calls greet with the parameter I plus one passed by value。

Did I do something wrong? I plus one。 Oh, not I plus I。 That would have been an interesting program。

Okay。 All right。

I plus one。 There we go like that。 Okay。 And then that's that。

And then we need to join them for size。

I'll just do this。 It's going to be the same。 Okay。 For size t。 K-nom-greeters。 Okay。

And then we just do a greeter。 Nope。 Actually in this case we don't want to do that。

We want to do the actual thread。 I can't do it the way I want it to。 I guess we probably could。

We could。 But in this case we'll do it the way we did before。 And then we want to do the thread。

And percent greeter。 Mix and match。 Greeter。 That。 And then in this case we want to do greeter。join。

Okay。 That will wait for them all。 Okay。 And then we want to just say see out everyone。

All greeted out。 And out。 Okay。 That's what we're going to do。 Good question。 Wait。

why did you say that original form of--, The original form of wood have worked。

I just decided to do a different way。 The original form of wood would have been this。

We could have done the--, We would have just done the greeter's eye。join instead。

That would have worked。 Yeah。 But different way of doing it。 Let's show you both。 Question。

What happens if you joined right after you created it? Good question。

If you joined right after you created it, what would happen? You tell me。

Because it was a good thing to do nothing so it wouldn't get like, different time。

It would do something? What would it do? That would--, Won't join and wait until that thread just--。

Yeah。 It would serialize everything。 If you create the thread and then immediately join。

then it won't, go on and it won't even go to the next loop until that thread finishes。

So you need two loops? Because you want to launch all the threads。

Let them all work in parallel and then wait for them all afterwards。 In fact。

that's a very good point。 That this is actually kind of key。 This is very much non-blocking。

This loop happens super-duper fast。 What happens? It just basically calls this thread function with a function name and。

says on your own time, as soon as possible, launch this thread and it。

moves on to the next iteration of the loop。

So there's no waiting at all on this and that's actually kind of key。 All right。

Let's see if we did this right。 Make readers。 So far so good。 Greeters。 All right。

And then Greeters。 There we go。 Okay。

So remember it's wait pausing a little bit in there。 That's why it does that。

How many times should six say hello? Six, say hello six times, I believe。

unless I did the less than instead of。

less than or equal to。 But anyway, that's what it is。

One only says hello once and then one happens to be the first one that's。

issued all of its hello so that they go home and so on and so on and so forth。

Yeah。 If you want to do all that, I think we're going to do all that。

Could you do an OS lock for the --, Okay。 So first of all, OS lock is not for processes。

It's only for C out。 So OS lock is only -- we'll talk about locks and things in a minute as we go for it。

If you wanted to do it in order, you just probably wouldn't have threads, number one。

And number two, if you really could just join after you do each thread and then, it would do it。

That's not really the way you would normally use threads anyway。 All right。

Questions on this one, on this greeter function。 You can actually pass the parameters in。

Yeah。 I'm Mark。 [ Inaudible ], Oh。 [ Inaudible ], Right。 There's a good question。 The question is。

wait a minute。 And I think you're alluding to a wait PID where you can say, oh, this one ended。

I'm going to handle that one first。 Not really。 In this case。

you just got to -- you have to wait for them all in kind of order -- in whatever, order you want。

But there's not really a way to -- as far as I know, I'll look it up。

But I've never seen it where you've waited for things out of -- you've joined out of order。

You kind of just go, I'm going to slouch these threads and then wait for them all at the end。

In some sort of order。 You can't say I'm going to wait for any thread like that。

It's not the same model as multi-processing。 Good question。 Okay。 All right。

So that's the greeter program。

And notice you can send in a parameter。 Okay。 All right。

What we can do is we generally want to use threads to break up a smaller -- or a bigger problem。

such that some group of threads can work on individual parts of that problem independently。

That's why we have multiple cores and multiple processors so that we can actually do this。

It's actually a nice benefit of threads and probably the main reason threads were ever created in the beginning。

So what we're going to do is we're going to do another little program and we're going to model ticket sales。

Okay。 Basically, this is where you call up, like, I don't know, United Airlines in this case。

And you don't do that anymore。 People used to actually call them instead of doing it online。

But you would call them up。 And let's say we have 10 ticket agents, which I'll answer the telephone。

And when they answer a telephone, they sell a ticket。 And we have 250 tickets that can be sold。

Okay。 256 tickets can be sold。 And as they answer the telephone。

they get a caller and then they sell that ticket。 Okay。 And they sell one ticket。 Now。

it might turn out that somebody wants to fly to the middle of nowhere or whatever。

And the ticket agent takes a little longer to actually process that ticket。

And it might be that some other ticket agent gets a whole bunch of people who just want to fly from New York to Boston。

And it's an easy, like, you know, two second call, phone call, whatever。

That's -- so we don't want to necessarily limit all of the -- we don't want to limit -- oh, no。

All right。 Why do you do this? Hang on。 We don't want to limit the actual number of tickets to 10% of the -- like 10% for each agent。

Okay。 And specifically, so that we can allow them to do it a little more effectively。 Okay。

So what we might do is something like this。 Okay。 Let me actually start -- I'll actually do this in the program because we're going to run this for you in a minute。

And let's see if I can clear it。 Okay。 Let's see。 This one is going to be called Confused。

And we'll see why in a minute ticket agents。 Okay。 It's going to be a little bit confusing here。

But this is what we might start out with。 Okay。 We might start out with something like this。

All right。 Thread Agents 10。 Okay。 We could have a constant for that。 That's that。 Thread Agents 10。

Size T remaining tickets。 Tickets equals 250。 Okay。 So we're going to have 250 remaining tickets。

All right。 And then four。 Size T。 I equals zero。 I is less than 10。

I plus plus。 Okay。 What we're going to do here is we're going to say agents。

I equals thread ticket agent because that's the function we're going to create。 Ticket agent。

We'll give a ticket -- each ticket agent a little number between 101 and I。

I'm not sure why we gave it a hundred and one, but we did。 Okay。 100 and I。

And then we need to pass in the remaining tickets by reference。 Okay。

Now thread has no idea about a reference parameter versus a regular parameter。

So if you tried to say this remaining tickets like that, okay。

then what would happen is you would actually send it by value, which is not what you want to do。

Okay。 And there's no real easy -- well, there is an easy way to do it。

but something you may not have seen before。 If you want to specifically send a parameter by reference。

you actually wrap it in a ref function。 Okay。 Or maybe it's an operator。 But anyway。

it basically says send this by reference, not by value。 Okay。

So that's what we're going to do for each thing because now we're going to have one remaining tickets that all the agents are going to pull from。

Okay。 All right。 And let's see。 That's -- let's see。 Is that the end of the function there? Well。

we've got it。 We've been running that statement。

Okay。 And then there, and then that actually launches them all。

And then we have for the red ampersand agent in agents, agent dot join like that。

And then we will say see out end of business day。 Okay。

So we're basically going to -- after everybody finishes, we will say end of business day。 Okay。

All right。 Everybody get what's happening in main there。 Okay。

Let's look at the actual remain or ticket agent function and see what we can come up with here。

Okay。 Well, we're going to have each agent grab a ticket and sell it, basically。

And then we're going to have a ticket that's going to be like a while remaining tickets。

It's greater than zero。

Okay。 Well, in here we are going to say I have a function that handles the call。

Okay。 You don't have to worry about the points up here somewhere。

You can actually read it later。 But don't -- actually。

it's just basically -- it sleeps for a while to kind of mimic handling the call。

Okay。 So we're going to handle the call。

Okay。 And then this is going to sleep for a little while to basically mimic handling the call。 Now。

how long it takes。 And that's a random number。 Okay。

And then we're going to say remaining tickets minus minus because we just sold one。 Okay。

And then let's actually print out OS lock in this case and agent number。

And same thing ID sold a ticket like that。

Okay。 And then we will say remaining remaining tickets more to be sold like that。

And then we have to do the OS unlock like that。 Okay。 So we're basically saying, okay。

well now a ticket -- ticket has been sold。 Okay。 And then we're going to let the agent take a break if it's time for them to take a break。

This is just to kind of interleave things a little bit more。 If should take break。

then we are going to basically take break。 Okay。 All right。 Sometimes these get a little bit silly。

But then that's it。 And then see out。 That is actually after the while loop, right?

We get -- then we say, oh great。 We've sold all the tickets because the remaining tickets are not -- there's no more left。

Okay。 So we can say something like agent number ID notices。 All tickets are sold and goes home。

Okay。 And LOS unlock。 I forgot to do the OS lock beginning。 OS lock that。

Okay。 All right。 So this is our first attempt。 Anybody see any bugs? All right。

This is our first attempt。 Basically we're going to have each agent keep countdown and go ahead and do it。

Make confused。 Ticket。

Agents。 Oh no。 Here we go。 Let's see。 No name type online。 Oh boy。 I love C++ errors。 Okay。 Hold on。

Not there。 Where is the actual error? Not there。 Not there。 Not there。 Not there。 Uh oh。

Oh there it is。 62。 Maybe。 62。 All right。 What do we do here? We're going to do a red ticket agent。

We're out for remaining tickets。 Agents。 We set up agents。 And then what else? It's the agent side。

It goes through the ticket agent。 And plus the ref remaining tickets。

I think you don't define ticket agent。 I don't define ticket agent。

Uh it's up here isn't it? I think there it is right there。

It's the function we're trying to send。 Mmm。 Let's see。

Do we need the ref? We do。 That's right there。 That's going to happen there。 We do need that。

You need to like import something。 I don't think so。 Um let's see。

I'm trying to think if there's any。 Let's look at the error again。 No name type。 Mmm。 Red 137。

Known type name type in class void。 Not sure。 Well in the interest of time I happen to have the original。

Let's just do it。 Let's do it。 This is just in case this sort of thing happens and I can't not。

good enough to find this。 I'm confused ticket agents original CC。

Uh let's see。 What did we do here?

Oh no。 Doesn't look looks much different。 I don't think it。

Well it's the one I promise you it should be the one。 Now that's a hang on。

That is it uses all the constants。 Yeah。 Maybe I actually forgot a part of it here。

No I don't think so。 Well here's what we're going to do。

We're going to copy it over and do it and run it。 Uh because I'll look at here。 Here's what I'll do。

I'll make a copy of it right now。

Confused ticket agents。cc to confused ticket agents。 Chris is confused。

I'll check it out later and then confused ticket agents。

Uh original to confused ticket agents。cc。

And right。 Okay。 Let's try it again。 Confused ticket agents。 That time it worked。 Okay。

Now I swear I work when I try to。 Okay。 So here's what it's going to do。

It's going to go count down all 250 each。 Each ticket agent is getting waiting some amount of time。

Some may get five over the course。 Some may get 10。 Some may get 20。 Some may get 30。 Whatever。

It will finally keep going and count down。 And then that's going to take a long time to get one more sold。

Okay。 So and this is going to go on basically forever。 Right。

In fact when it gets down to about zero again it'll probably wrap around again。 Okay。

So we have an issue。 What is our issue is the question。 Okay。

Let's look at the actual code here that we should have been doing here。 Okay。 That's main。

And then that's main two。 We already know that's there。 Let's see。 Here's the ticket agent。

Maybe I still looks all right。 So here's what we're doing here。 Is there a race condition?

Some of you already noticed it。 I could tell when I was writing it。 What's the race condition here?

Yeah。 Emily。 They all have access to remaining tickets。 Right。

And when the remaining tickets when it does the decrement there and the remaining tickets。

while remaining tickets is greater than zero and then remaining tickets minus minus。

those it may not be true that that actually that remaining tickets is still greater than。

zero because some other thread could have come in and done the exact same thing。 Okay。

So that's the issue。 One of the issues here。 In fact, it's much more subtle than that even。 Okay。

There's this idea that you can do that。 Well, what happens is, well。

we can see what happens there that it actually ends up going。

down and wrapping around because two things have tried to decrement。

If actually indeed decremented the thread count before, another thread came out and said, oh。

there are any left。 Oh, yeah, there's four billion left。 So that's the issue。 The, as I said。

it's even more subtle than that。 Even remaining tickets minus minus itself is not necessarily atomic。

Okay。 A C++ statement is atomic in that it runs on its own。 But remember。

what does a C++ statement do? And all you people who took 107 would know that it gets translated into possibly a whole bunch。

of assembly code instructions, each one of which is more or less atomic, but together。

they certainly are not atomic。 Okay。 So this, when you do remaining tickets minus minus。

if you go and decompile, you can see, that it ends up coming into some。

ends up something like this assembly code here where you've got five different, instructions。

any which, any one of which could get interrupted by going to the next thread, and having a problem。

So what are we actually doing? We're getting the memory and then we are decrementing it by one。

Remember our favorite command, LEA, for two people who took 107。

And then we're decrementing it by one。 And then we are moving it back into memory。 Well。

if any other thread did some of those operations while these were happening, like。

then the memory wouldn't necessarily be one or move。 This is a classic race condition。 Okay。

Absolutely classic race condition that you need to be able to handle。 Okay。

So what are the kinds of things we can do? Well, what we can do。

and this is built into the thread libraries, we can say, okay, one, of the programs is not going to。

is not going to run, or one of the threads is going to have, access to this data structure。

in other words, the, or, or, or data structures that remain, in tickets variable。

while all the other ones have to wait on it。 Okay。 So that's what, and by the way。

I just realized what I can do here to make our code a little, simpler, as far as the, see。

then confused ticket agents, not CC。 I'm just going to do the following。 That was which one。

That was the ticket agent。

Again, I'll go home and pour over this and see why I made this mistake before。 But anyway。

we'll place that in there and then we will change main as well。

And then let's see, name is going to be here。 Okay。 And what we're going to do is, okay。

So let's see if that works now。 Make confused ticket agents。 Oh, no。 It must be my, wait。

I swear I typed this in the front。 Well, all right, hold on。 Maybe we won't do that。 Well。

what I'm going to, I'll show you what we're going to actually do here to, yes。 All right。

What we have to do is we have to modify using this thing called a mutex。 Okay。 And a mutex is。

where did it go here? A mutex, this we already went through this。 A mutex is a。

basically a lock that says when a particular thread calls lock on a mutex。

Any other thread that also tries to call lock on that mutex has to wait until the previous。

thread releases it with an unlock。 Okay。 So here's how it goes。 It's a class called mutex。

And what you do is you say mutex m for some mutex variable。 Okay。 And then if a thread says mutex。

lock, another thread can come in and say mutex。lock。

But if the previous thread already has it locked, the new thread just kind of waits around until。

it unlocks。 Okay。 If multiple threads are trying to lock on a mutex。

then one of them will win during the, unlock battle that happens after it gets released。

And all the others will go back and wait again。 Okay。 So it's a way to say, no。

only one thread can handle this at a time。 And it's just using this variable as that beacon that says you are allowed to use it。

you are, not。 And that's how it works。 Okay。 It locks what we call critical regions of memory。 Okay。

And it's super useful。 And there's a couple of other ones that we're going to learn about as well。

Super useful to be able to say, oh, great。 Now we've got multiple threads here that all get to use that。

but nobody's going to step, on everybody else trying to do the decrement。 Question chasing。

Is it possible that thread will always lose the battle? So that's a good question。

Is it possible that a single thread will always lose the battle? Sure。

One thread might always lose the battle。 But then in the end, if all, whenever else finishes。

it will finally get it。 If your logic is right, it would eventually get it。

But you can't promise to any one thread, you're going to be next。 No way to do that with a mutex。

Good idea。 There are certain types of locking locks that have a priority associated with them。

We'll get to that later。 But the ones we're going to concentrate on have no。

you have no ability yet。 Yeah。 So that's C++ statements on atomic。

Because they're translated into assembly, the same culture to C++。 Yeah。

The question was are C++ statements are not atomic because they're translated into assembly。

Same exact history for C。 Yeah。 All of that stuff that you did in 107 where you had to translate C statements into assembly。

you had lots of assemblies, statements per C statement。 So same exact thing。

So does it not get expensive instead of just getting hung up on this light log? Ah。

Very good question。 Does it get expensive because if they're held up a box?

Let's see what happens when we make a change here and then we'll see how it actually manifests。

itself。 A very good question and very pressing a question。 Anybody else? Okay。

So what is a mutex come from? It stands for mutual exclusion and it basically is solely there so that you can mark where critical。

places in your code are that multiple threads might be working on at the same time。 Okay。

When you create a mutex, it is unlocked。 A particular thread calls lock on it and then nobody else can call lock。

They just wait until that lock is unlocked。 The only thread that's allowed to call unlock after locking is the one that locked it。

Another thread could do it and it's undefined behavior and you shouldn't do that。

Only your heavy logic so that nothing tries to unlock a lock it doesn't hold。

You'll see how that's not hard to get that right by the way。 And then that's that。 Okay。

So let's actually see what we would have to do to make our confused ticket agents。 Uh, hang on。

Confused ticket agents。

Program。 Do this。 Okay。 All right。 So here's where we've got the issue。 Okay。 Well。

what we need to do is we need to have a mutex that we send in to all of the threads。

So they all have one mutex。 Okay。

So let's go back down to main and actually do that。 Okay。 So, again。

what we're going to do is now we're going to say, oh, all right。

Let's have a mutex here that is, let's say mutex。 Let's see。 I haven't done the, yeah。

I haven't done the before。 Yeah。 Mutex tickets lock。 Okay。 All right。

That's all you have to do for that。 Okay。 And what we can do is we can, um, in here。

we actually now have to, uh, send it in。 Okay。 So we're just going to do this。

We're going to say same thing as before。 We're going to say, uh, have the remaining tickets in here。

And then we're just going to put another parameter, which is the same thing as before。

ref tickets lock。 Okay。 Like that。 And then that will just send the other parameter in there。

We have to update our parameter list for the function as well。 So it can take the right function。

Okay。 And, um, other than that, that's all we have to change in main。

We're just creating in main and sending it into the threads。

Okay。 Now here we need to do a mutex reference。

Mutex reference tickets lock。 Okay。 All right。 And what are we changing here? Well。

instead of doing, uh, remaining tickets is greater than zero。 Let's do the following。 Okay。

Let's do while true。

Okay。 And then let's do this tickets lock dot lock。

Okay。 Actually, I'm going to reform at this。 There we go。 Ticket lock tickets lock dot lock。 Okay。

And then we are going to do, we're going to check and see if remaining tickets, each。

equal zero, right。 In other words, now we're the ones who are going to have access to that remaining tickets。

Nobody else can be changing it in the middle。 We're going to be changing。

We're going to be checking it。 If it happens to be zero, we're done。 No more tickets to go。

You have a question。 So there's a lock lock all of the variables in this function or where did you have to。

then you have to specify the remaining tickets to one model。 Yeah。 Good question。 The question was。

does lock, lock all the remaining variables? It's much more simple than that。 Lock says。

somebody else is going to want this exact data structure or want this critical, piece of code。

I'm going to just basically put the lock in here。 Nobody else can access this critical piece of code because they're also going to ask。

for the lock。 Okay。 Anything else, they're going to be doing the same sort of thing。

So you have to get your logic right so that you know that, oh, everybody who wants this。

critical data structure or in this case remaining tickets, just lock around it and then that's。

all we need to do。 Okay。 So what do we have to do after the actual while statement here?

Tickets, lock, unlock。 Right。 So we have to do that if we're going to break out an unlock it there and then at that point。

everybody can do it。 We're not quite done yet。 You have a question before we go。

So is it that at that point they can lock? None of the threads will run after that。

So all the threads are going to eventually get to this line。 And they're going to go。

I want to lock that variable。 One of them is going to win。

All the rest is going to wait around until this one finishes。 Okay。 Now this may cause an issue。

We'll talk about that。 They're waiting at that。 They're waiting at that lock。 Yep。

They're not doing anything。 There's waiting at the lock。 Yep。 Yeah。 Good question。

So specifically because of that break we break out but we're still locked so we better unlock。

But as I said we're not quite done yet。 We also want to unlock actually let's say I guess we could do it either there or let's。

say not there。 Let's say we want to lock like unlock right here。

On tickets lock unlock like that as well。 Now it will actually work out because what we didn't want to do is re-lock try to re-lock。

the lock。 Again you would actually deadlock because you've got the hold of the lock and then you try。

to lock it again and you're going to wait until you release it and that's impossible。

But that's so we do it in two different places in this case。 On next。

I guess on Wednesday we will learn about another, we do that or next week。

We will learn about another way to do it where you don't need both unlocks。 It's a different。

actually a very clever piece of code。 We'll learn about that later but that's why we do it there。

Okay。 So I believe that is all we need to do at this point to make this now safe。 Right? Question。

The original one that calls lock。 Once they call unlock the different threads come around and call that lock and when you。

press right。 >> Yeah。 The question was if once you've got lock when it unlocks another thread can come in immediately。

if it's waiting for that lock and then unlock it。 And two or more could be trying to do that and one will win。

And the other one will go back and stay locked for a while or stay in that position。

There's no race condition。 Well, I mean there is a race condition in that they're both looking for it but hopefully。

your lock doesn't matter at that point because you're not trying to change something in there。

You're not trying to serialize it or so forth。 Okay。 So let's see what happens when we do this。

Make confused, ticket, agents。 These are not confused anymore, hopefully。

Let's see what happens when we do that。 Confused ticket agents。 Okay。 Now。

what's the first thing you notice about this? >> Slower。 >> Slower。 Right。

So there is an issue here because we've now got like every ticket agent is saying, okay。

there's a ticket available。 I am going to go and I'm going to sell it。 Right。

And then I'm going to decrement the remaining tickets and then I'm going to release the, lock。 Okay。

And this, the way what we've created is kind of inherently still serialized。 Right。 Now。

there is the break time which is not serialized。 That's fine。 I mean。

we get a little bit of benefit because we don't do the break time。

Anybody can be on break at the same time。 They don't fight over the coffee machine or something or whatever they need to like。

you know, do on that。 But otherwise it's taking a while。

This is taking like over a minute actually to run this now and you think, well, that seems。

a little crazy。 Okay。 So is there a way to actually modify this so that we can do this and still end up not。

blocking in quite like in a way that makes it so that we're slowing things down? And by the way。

let's make sure, let's see if it works。 We're going to get down to zero。

It should be that everybody goes up。 Okay。 Yay。 We worked。 Right。

Because they all got to a point near the end where they, there were no more to be sold。

And by the way, you, let's see, is it saying or no? They all, they all wait。

Notice the tickets are sold。 They actually all happen to wait at the same time in that case。 Okay。

And then because or they all ended up that place because they were all waiting for that。

lock to do that check。 So what do you think? Can we fix this even to make it a little bit faster?

And maybe we have to change our own like understanding of what it means to sell a ticket。

Like sometimes you can't get away with that。 All right。 So in this case。

could we do something here to make it so that this actually works and, faster anyway?

What do you think? Awesome。 Like put the remaining tickets there and then do。

I'm going to go this way。

This one up here。 Would that work? I think it would work。 Right?

Now we're assuming that the ticket gets sold。 Right?

So let's say the ticket agent gets on the phone。 The ticket agent says already said, oh。

I've got to say it's sold。 And then let's say that there's some logic where the。

I don't want that stupid ticket, airlines terrible to hang up on the phone or whatever。 Right? Well。

they have to somehow put the ticket back and we don't have that logic in here, but。

we could build it in if we, I mean, we, we could build it in。

You could build it in somehow where we say, oh, look, if you've got the last ticket, you。

better keep trying to sell it。 You've got to stay until it gets sold。 There's something like that。

Right? But I think this will actually, this will actually work。 Let's see。

make confused tickets and then, ah, still much faster now。 Right? Not, not like way, way。

way faster, but definitely much faster。 I can say it is much faster。 In fact。

and this is going a lot faster。 But let's see what happens near the end here。 See if it still works。

There we go。 Okay。 So it did finish and lots of agents said zero more tickets and they let。

they went, home while the other take, agents were still selling。 Right? But who cares at that point?

We assume that each agent sells their ticket。 Okay? So that's one way to do it。 So what you read。

the takeaway on this is if you can make your logic such that you lock。

for a very short amount of time, that's the best way to do it。

Don't try to lock around important things like handle call。

It has nothing to do with whether or not the ticket itself has been decremented or not。

Now maybe we need more logic to put it back if it doesn't get sold and, you know, that's。

a different, different program in some sense。 But you do have。

you do want to not lock around extended places that don't matter for, that lock。 Don't。

don't have the ticket agent say, you can't touch the ticket queue until I'm done, selling my ticket。

which is what we just did。 Yeah。 So in that like agent blank sold the ticket and there are like multiple of those statements。

of like different agents and then the same number of tickets remaining。

Like because right now the lock is going to lock into that, you know, a decrement can。

happen before the print statement happens。 Right。 So like right here it says six more tickets to be sold from two different agents。

Is that your concern? Yeah。 In case it probably doesn't matter, you'd still have some other。

maybe we should have put the, print statement before they're unlocked or something if we wanted to。

We could have done that too。 But that's just a print statement in that case。

We could probably clean that up。 In fact, we can try and see if it makes sense for our thing here。

Let's see。 Yeah。 I mean you would have to break it up here and say, you know。

put the number of tickets to, be sold up farther and the agents sold the ticket below。

You could break that, see out statement up and do that。 But yeah。

that's another race condition in that sense。 Oh, two people think that there's the same amount of tickets left to be sold。

That seems a little odd。 But in the end, their logic for the stopping still works out。 But yeah。

that was kind of another anomaly that you'd have to fix。 Yeah。 Good question。 Okay。

Is there any way to make it near as fast as this won't be written lock at all?

Is it like even that is not as quick as it works? Is there any way to make it near as fast?

This is pretty close actually。 I don't know if you remember from before。 It's about it。

It's pretty close。 But no, I mean, locking will involve some overhead because there is a time where two threads are。

trying to get the same data structure or variable and it's going to take more time。

And that's just a, that's a downside but a necessary downside of making it so it's functional。

So the program works。 But yeah, good question。 Yeah。

What we do do like conditional lock boards like the only lock where they're like 10 tickets。

left to the board。 Yes, good question。 So Sam saying wait, what if。

is there a way to lock when there's like a certain number。

of tickets left or something so that more people can go in and tell that there is, they're。

called conditional variables actually。 And there's also another one that will build ourselves called a semaphore which does that。

It allows you to basically have a count in which case you only lock when that count ends。

up at zero。 And then before then anybody can go in and grab as many as they can grab one until that。

count becomes zero。 Good。 Good point。 We'll get there。 Good question。 Yeah。

Could you explain to you how the lock calls inside the lock is slower? Yeah, good question。 Well。

why is handle when handle call was inside the lock, why is it slower? Well, handle call does what?

It just is asleep。 Right。 And if all the, if you have not unlocked yet by the time when handle call gets called。

everybody, else is standing or looking at that one ticket agent trying to sell the ticket because they。

can't get into the to modify it。 Right。 So there's where the inherent like slowness happened before。

Every ticket agent was on and on their own selling a ticket with all the other ones waiting。

around going, I can't even sell a ticket because I can't even get out the thing。

So our model wasn't perfect in that sense。 Right。 But knowing that a ticket's always sold makes it so we can do it。

Okay。 Other questions on this? All right。 Threading is kind of fun stuff。

but you can see lots of issues。 We will get to many more different types of locks as we go along。

All right。 I will see you Wednesday。

P12:Lecture 11 Multithreading, Condition Variables and Semaphores - main - BV1ED4y1R7RJ

Alright, we might as well get going, but it seems a little odd。 I'll try not to blaster yours out。

Okay, so definitely the day before midterm。 We've got lots on your plate。

I can see why some people might not be showing up。

Wouldn't it be terrible if this was the day that the screencast failed?

You guys all here would be happy, but everybody else wouldn't be I suppose。

A couple things。 First things, no labs this week。 They're instead。

we're just asking that you watch a little short video。 It's five minutes long。

Some of you have actually already seen it。 If you took CS106A maybe like last summer or two summers ago or something。

you might, have also seen it。 But anyway, it's a short little video。

literally five minutes and it's other graduates from, Stanford mostly who have。

we're just going to talk about CS a little bit。 So give it a watch。

That's going to be your lab check-in for the week。 Otherwise。

you probably want the hour and 20 minutes back to study, do work on them, Stanford, Shell, etc。

Alright, so enjoy that and that's what we'll be instead of lab for this week。

The other day, what did I do? I got confused。 So I teach another class。 Is Mark here?

I don't see him。 I teach another class and a couple people in this class are actually in that class as。

well。 I'm teaching computer science, an introductory computer science class。

Now you literally used the example from what happened in class the other day as I was talking。

about, hey, guess what? Sometimes professors mess up and they。

or they can't fix their live code or they can't, find the bugs and this is exactly what happened on Wednesday。

So I apologize about that。 But as promised, I did go back and figure out what was going on with that compiled bug。

It was a little bit subtle。 Not to say that was a good reason why I didn't figure it out but it was a little bit subtle。

as to why the error happened。 Here was the code again。 This was the error。 We were focusing。

I was focusing in the lecture on this line right here。

Ticket age is line 62 near the end of the line actually which is this line right here, near。

the end here。 So let's say right around here。 That turns out to be where the issue was。

What are we doing here? We are sending a reference to remaining tickets to a function。

to the thread function which, will repackage it and forward it on to the ticket agent function。

So that's kind of what's going on there。 There's a little bit of C++ black magic under there that happens at that point。

But if you look up here, we've got ticket agent and so this is what's going on with the remaining。

tickets there。 Anybody notice what might be the issue at this point?

It wasn't that I just passed it。 I did pass it correctly and it is a reference。

The problem is that little error message just told me to look here。

It didn't say anything about up here and it also explicitly didn't say anything about, a type issue。

So what's the type of remaining tickets? Size T。 What type did I tell this to look for?

Unsigned int which is kind of like a size T except an unsigned long is what a size T, is。

So the compiler went, "You're trying to do two different types here。 Give me a break。"。

Then gave me some terrible error message about that。 Anyway, that was the issue。

Once I went back and fixed that made it size_t, ampersand then it worked just fine。 Yeah。

so bugs happen to everybody who was ever programmed so don't think you're on your。

own when you hit all those bugs。 They're tough to find and they're tricky。

I had somebody in Offsiers yesterday who had a bug that honestly the only reason I figured。

out what it was because I have 25, 30 years worth of program experience。

It was such a subtle little bug that I thought, "Oh, that one's a hard one to find。", So anyway。

it was a good thing that that person came into Offsiers。 But these are things that happen。

It's just, you know, bugs are hard, programming, challenging。 So anyway。

there's what happened the other day。 All right, so let's move on to a very cool problem that if you take a multi-threading。

or even a multi-processor type class like this one in particular, if you don't cover, this problem。

it's kind of like never ever reading a Shakespeare play in an English class, somewhere。

Like this is like the quintessential deadlock problem for multi-processing, multi-threading。

sort of situations。 It is called the Dining Philosophers。

It's actually got a fun name and it's Dining Philosophers。 Here's how it works。

Let me see if I can get my whiteboard here going。 Here's how it works。

There is a table and in the table, okay, my drawings those are terrible by the way。

That's supposed to be a circle。 There's a table here。

There are five philosophers and philosophers have like big brains or whatever。 There's one, two。

right? Okay, three over here and then four sitting at this table here and then of course they're。

not, I guess there should be, they should be even there。

You can see why I was an art major when I did this。 Okay。

five philosophers sitting around the table。 They each have bowls of like spaghetti in front of them。

Okay, they each have bowls spaghetti and they want to do a few things。

They want to both think and then eat。 Okay, and this is what they do。

They're going to actually go through this three times。

They're going to think for a while and then they're going to eat and then they're going。

to think again, eat, think again and then eat three times。

And what the situation here is is that there are one, I'm going to just kind of draw them。

like this。 Two, three, four, five forks。 Okay, those are my little forks。 Okay。

and here's how the dining philosophers eat。 If you are, if you are about to eat。

you grab the fork on your right and then you grab。

the fork on your left and for some reason you eat spaghetti like this, right, which is ridiculous。

This should have been, whoever did this should have just used chopsticks because that actually。

makes sense and that's a legitimate like you need two chopsticks to eat, right? But anyway, well。

the original problem was forks because whoever did it had possibly, never heard of chopsticks。

I don't know。 But anyway, point is that it's forks in this case。

but they each need one and they're going, to eat for a little while and they are going to。

when they're done eating, put the forks, down in the same order。

they pick them up in like right and then left and then, or I guess, in that。

here's the option or we'll see what the code does anyway。 But the point is that's what's happening。

Now, in the end, this, if you do this, like how many people, how many philosophers can。

actually eat at the same time? Two, right? Like that's the maximum, right?

Even though they all might be going for the forks。 Okay? So that's the big issue。

Like there's two people that but we want to make it so that they all kind of share this。

because we don't know when they're going to be thinking when they're going to be trying。

to eat and that's that。

Okay? So that's the setup for the problem and what we're going to do is we're going to just write。

a little code to try to test this out。 Okay? And try to actually write this。

model this in the in-ceivos-phosome。

In fact, let's just go straight to code and we will do this。

Dining philosophers with deadlock which means we're going to probably cause an issue here。

Okay? So here's what's going to happen。 All right, let's start with how a philosopher thinks。 Okay?

A philosopher thinks by basically in our program basically just taking some time, some kind。

of random amount of time to actually go ahead and think。 So we're going to do something like this。

Remember we have, we're in threading mode。 This will be in a thread by the way。

Each philosopher will get its own thread and we're going to do the OS lock。 Okay?

And then we're going to say something like, let's see。 The ID。 Okay?

That's the philosophers ID that we're passing in here。

Zero through four because there's five dining philosophers。 Okay? ID and then starts thinking。 Okay?

And when the philosopher is thinking then we have to spend some, we have to actually take。

some time to do this。 So we're going to do that and then。

and L and then we are going to do OS on lock like。

that。 Okay? And then, I don't know why it never, never, well anyway, it doesn't do the indentation。

for whatever reason sometimes。 Okay。 So anyway, we're going to, we're going to print that out。

Then we are going to do a sleep。 Now in a thread you call sleep four and then some number of like milliseconds。

generally, it's milliseconds。 So let's sleep for some amount of time。

We have another function that's just going to kind of randomize it。

It's not really important what it is, but we're going to just get think time like that。 Okay?

And then once it's done sleeping, we're going to say same thing, OS lock, ID, done, all。

done thinking。 Okay? And L and OS on lock。 All right。 That's all the philosopher has to do to think。

Okay? Pretty straightforward。 All right。 All right。 And to eat。

we're going to use our mutex that we learned about last time。 Okay? Remember what a mutex is。

A mutex is a data structure such that many different threads might be trying to grab。

control of that lock in the data structure。 The other ones, if they。

if it's already locked when they try to grab control, they just wait。

around until it unlocks and then they try to grab it again。

And at that point you might have contention between the two。 So because we've got two forks。

we're going to have a left fork and a right fork。 And we are going to have the philosophers actually try to eat。

So what we're going to do, they're going to pick up the left one as it turns out first。

I think I might have said right before。 They are going to do the left lock。

Okay? And then right lock。 Okay? And then we're going to say, okay, look。

At this point now they can start to sleep or they can start to eat, which is kind of sleeping。

in this case。 Okay? So they're going to say, oops, ID starts eating。 Okay。 Nom, nom, nom。

nom like that。 Okay。 And L and then OS on lock like that。 Okay。

And we're going to model this by again doing some sleep for thread wave sleeping for get, each time。

A slightly different one which just basically says some amount of time that is going to。

get in fact, I just realized I actually didn't call the function up here。 There we go。 Okay。

So we're going to do sleep free time。 And then we're going to say, see out OS lock, ID。

all done eating。 And L and then OS on lock like that。

And then of course now we're done eating so we can put the forks back down。

We are going to do it in the same order。 We picked them up and as it turns out。

So left dot unlock and right dot unlock。 Okay。 Now we are going to have to pass the forks that are next to the dining philosophers in。

there。 Right?

So if you looked at that circle again before, right, it's whichever one, whichever two forks。

were next to a particular philosopher here and here is the left for this one and the。

right for that。

Okay。 All right。 So that's how that works。 And that's the, that is the eat function。

Okay。 Let's go and look at what a philosopher actually does。

Okay。 A philosopher does the following。 The philosopher is four size t i equals zero。

i is less than three, i plus plus。 Remember, they're going to do this three times in a row。

What are they going to do? They are going to think and then they are going to eat and we have to pass in the left。

and be right。

Okay。 So far so good on what's going on there。 Okay。 That's what's happening。

That's a philosopher。 Now we have to do our main function here。 Okay。 And the main function。

we have to set up these mutexes for each fork。 Okay。 So let's do that。

Mutex forks will do five of them。 Okay。 And then we'll have philosophers。 We'll have five of them。

Okay。 And then four size t i equals zero, i is less than five, i plus plus。

Okay。 And in here we're going to actually launch each thread like we've done in the past before。

here。 Okay。 What we want to do though first is we need to set up which who gets what fork, right?

So we can do mutex left, oops, left equals forks i。

Okay。 And then we can do, oops, maybe I need a semicolon there, mutex right equals forks i。

this way, i plus one, mod five。 Right? That'll just give you the one around the corner。

the other side。 Okay。 The mod in there wraps around if necessary。

Okay。 And then we actually have to do philosophers i equals threads。

And now we're calling thread that we are, or we're setting up a thread。

Philosopher was the name of the philosopher, I just believe, right? The name of the function。

And then we are passing in the id。 We are passing in a reference to the left。

a reference to the right。 And we are passing in a ref permits。 Okay。 And then, oops, sorry。

Not permits wrong, wrong, looking at the wrong one here。

We're passing in just a reference to the right, reference to the right。 And that's it。

That's all we need。 Okay。 All right。 After you do all the threads, you need to join them all。 Okay。

So another for size t i equals zero, i is less than five, five plus plus。 And you can just do。

can we do it that way? We'll do it the way we did before。 Like。

the way we've been doing it in the past was just thread, ampersand t equals, thread, ampersand。

well, call p for philosophers, doesn't really matter。 Philosophers and then p。join like that。

And then we can return。 Okay。 So that's our whole program。 And it's going to set it up so the world。

Let's see what happens when we run it。

Okay。 Make dining philosophers with deadlock。

Of course there's an error。 Of course there's an error。

Let's see。 [laughter], [inaudible], On 82 and 83 like that to me。 And that we are trying to, yes。

Thank you very much。 Although did that。

Oops。 Ah。 That make。 Ah。 Thank you very much。 Okay。 So anyway。

if we do dining philosophers with deadlock, if we do that, now you'll watch。

And it actually seems to go all right。 Okay。 We go, oh, our program is correct。 Right?

What happened was, let's look at exactly what happened here。 Okay。 Zero start。

They all start thinking。 And then three happen to finish first as it turns out because of the timing or whatever。

And then it starts, and three start eating。 So three grab the left fork and the right fork。

I should go back because you guys left them right。 And then did that。

And they all ended up working just fine。

Okay。 And you might say, "Hey, this is great。 We're done。"。

But what happens if we artificially put in a race condition that would like not be a。

very good, like it would make it so that they all try to start eating at the same time。

Okay。 What if we did the following? Okay。

What if we put a little sleep for, let's say five seconds in there。 Five seconds。 Right in there。

Basically saying all of them are going to do a little bit of waiting and they're all。

going to try to eat at the same time。 And then they're all going to grab their left forks at the same time。

Can you kind of see what's going to happen here?

Make dining for us strong and that。 Okay。 Well, they all get to a point where they all think and someone will get done in various。

orders but then they're waiting around after they pick up their left fork until each one。

of them picks up a left fork。 Well, what's the next thing they're trying to do?

They're waiting for them。

They're trying to pick up the right fork。 Right? So if we go back to our diagram here, right。

we've got this one。 Let's go from the top here。 This one picks up their left fork which is this one and then this one picks up left and。

then left and then left and then left。 And then this one goes and tries to pick up the right fork at some point。

Well this one is not there because the other ones are all waiting to pick up their right。

forks as well。 They just pick them up。 This is deadlock。 Right?

It's deadlock specifically because everybody's waiting on everybody else to be done eating。

because you've only got those five forks and there's five people vying for them。 Okay?

So do you see what the issue is? Right? The deadlock actually ends up happening。

Okay? And what we want to do is we want to try to avoid that。

Okay? Because when you have these kind of race conditions that lead to something like this。

you're going。

to be in trouble。

So we end up having to have to do quick that。 Okay? All right。

So this is all the code that we just just went through。 And here's an important part by the way。

We should be able to insert the sleep forecall pretty much anywhere in a thread and not have。

it matter to the rest of the program。 Like that's one of the ways to check for deadlocks and for other kind of race conditions。

You just say, look, let's artificially make it such that some weird condition happens where。

you could end up doing that。 Okay? Questions? [inaudible], Yeah, the good question。

Did we get lucky that it worked before? Yeah, you just kind of got lucky。

The odds are pretty good the way we set up the timing that it turns out that all five。

won't be trying to eat at exactly the same time until。 And even if one ever gets both forks。

well then they're going to be done eating and that, will open it up for others and so forth。

But there is that weird condition where they all go for the left fork at the same time。 Oops。

now you've got five forks and everybody's waiting for that other fork and nobody's eating。

and that's the issue。 Yeah。 Does lock ever return value? Does lock ever return value?

I don't believe lock has a return value at all。 I just think it, I think it's void。 If it does。

we really never use it。 There's no way to feel like, oh。

the fork's taken and don't think of it before。 There's a drop over it。 Oh no。

can you check a lock before you do that? In this case, it really wouldn't matter。 I mean。

because what are you going to do there? I mean, I guess you could, I guess you, if you, yeah。

it's not a bad idea。 I hadn't thought about that。 If you like somehow check and see, wait。

are all them taken? All of them taken。 If you keep track of them, yeah。

and that's kind of what we're going to do。 Like keep track of them in the bigger picture。 Yeah。

[ Inaudible ], Yes, the question is when you do sleep for 5,000, that's saying by five seconds。

everyone's, going to be done thinking trying to grab their left fork。 Okay。 And remember。

they only grab their left fork first, right? And they already did that。 Sorry。

they already all grabbed the left fork or at least they're trying to。

And then they're waiting around to grab the right one。 By that time。

everybody has grabbed the left fork and that's that。 Other questions? Okay。 We get this idea though。

This is the classic, like you will at some point again here, dining philosophers and go, oh。

I know what that is。 Right? I know all about that。 And that's where it happened。 So。

how are we going to actually fix it? Well, new Texas are going to probably be one way to solve this。

We just need to have some more logic in there to make it so that it actually works。 Okay。 So。

what we kind of talked about, it's impossible for three philosophers to be eating at the, same time。

right? You can only have two philosophers eating at the same time。

But you could have four philosophers trying to eat at the same time。

But as long as there are all five trying to eat at the same time, somebody will be able。

to get that other fork and start eating。 Does that make sense?

If you have four of them and the fifth one is just kind of waiting around not doing anything, yet。

at least one of those four will be able to eat。 It may not be two in that case。

but at least one of them will be able to eat。 Does that make sense? There's some。

I'd see some people going, "Oh, it doesn't quite。 Think that one through it for a second。", Right?

The diagram might help a little bit more。 Right? As long as one of them is not, you know。

say this one is not eating at all, not doing anything。

then this one will eventually be able to get its right fork。 Okay? All right。

So, that's so we can do that。 Now, we could do this in a couple of different ways when we're going to actually solve this。

You could say, "Well, let's just make it so that only two can ever eat at the same time。"。

That's fine。 I mean, that would probably be relatively efficient。

but you could also make the argument that, "Well, we don't know how long any of them are going to be waiting。

so let's at least, make the maximum number possible be able to eat so that it kind of continues through。

", There's arguments on both sides。 We're going to go with the preference to say。

"Let's make a limited amount of bottleneck, and it will just make sure that we can't do deadlock。"。

And that would be, "Let four of them vie for it。 Just make the fifth one have to wait。", Okay?

That's what we're going to end up doing。 Okay。 So, how are we going to do this? Well。

we are going to introduce an idea of a permission slip。 Okay?

And the permission slip is basically going to be basically saying, "Hey, there's going。

to be four permission slips。 The first four philosophers who get those permission slips are going to be able to try。

to eat。 They're going to be able to vie for those forks。"。

The fifth one just has to wait until one of the other ones is done eating。 Then we'll have that。

Okay? We're going to call it permits in this case, okay? Permission slips。

And we're just going to use a counter to count them up and down。

Okay? So let's actually look at this。 Dining philosophers with, we'll call it busy waiting。

Do we like busy waiting? No。

Well, we've got to get there。 We'll get to trying to fix that in a little bit。 Okay?

But let's see how we might solve this first of all。 Okay? Some of the things are the same。

So I'm not like, "Think is exactly the same。 I haven't changed that at all。"。

We're going to have a wait for permission and a grant permission。

But before we do that, let's actually set up main first。

Most of main is going to be roughly the same。 Okay?

But some of main is going to be a little different。 We're still going to have five forts。

we're still going to have five philosophers and so, forth。 Okay?

But what we're going to do now is we're going to say size t permits equals four because。

we're saying we're going to start out and there's going to be four permits。

And every time a philosopher grabs, like gets a permit, that number is going to go down。

And when it gets to zero, too bad, there's no more permits left。 Makes sense? Okay。

So that's what we're going to do there。 Okay? We're still going to have the forks set up like this。

Okay? We are also going to have another mutex for the permits lock。 Okay? Well。

if we have a permit and if we have this permit variable and any of the threads are。

going to try to modify it, we better lock around that so that they don't both try to。

modify it and we get a race condition there。 So that's another issue with race condition that we're going to have to fix that。

Okay? Same thing, we're going to start all them, we're going to get the left and the right。

Nothing's going to change there。 What we are going to change now is we're going to do the same thing。

Philosopher i equals thread as before。 Okay? We're still going to same function。

We'll write those in a minute or finish those up in a minute。

Perfur and i and a ref for the left and a ref for the left and a ref for the right。

And we are also going to have to send a ref for the permits because we have the permits。

and we're also going to have to send a ref for the permits lock as well。 Okay?

Because now we have to send more information so that they can actually use these permits。

effectively。 Okay? And then we're still going to join them at the end。

So is permits a sort of global variable for the permits?

Is permits a global variable for the permits? It's not a global variable but it is going to be passed to each philosopher so that each。

three is one。 Is it the same, it's the same actual variable for each one。

Remember threads can share variables in the same space。 So yes。

it's exactly the same one and that's how it's going to work because all the threads。

are going to go, hey, are there any permits left? Oh, I'm going to grab them。

And it's going to use the other。 Say again? When one philosopher takes the permit。

the other sees a change。 Yes, you'll see how that works but exactly。 Okay?

It's exactly what's going on there。 Okay。 So let's go change, modify some of these other ones here。

Let's go modify。 We've got think。 Eat is going to have to change a little bit。

Let's do that one right now。 Okay? For eat, we're basically going to have to get permission。

So let's create a function and we'll do that in a minute。 We'll say wait for permission。

Wait for permission。 We're going to pass in permits to this function and then we're going to pass in permits lock。

as well。 Okay? And then if we get a permission slip, then we can go and do our locking。

That's actually going to work just fine。 Okay? Then after we are done eating, we can say okay。

well let's give back like our permit。 So basically we can say grant。

We'll have a one called grant permission and this will be permits and it will be permits。

lock again, same sort of thing。

Okay? And then after that is when we can do the unlocking lock。 Okay?

So it's going to wrap those in the get a permission slip and then give one back。 Question。

I don't know if you have to unlock the permit after you go to sleep。

Like isn't it deadlock about locking the force? So could we unlock for a minute after you've locked our force?

Good question。 So the question is can we, could we unlock it after we've locked the forks? Well。

I guess you could but then you still might have a race condition in there。

You still might have a race condition where, let's see, in that, yeah, I think in that。

case you still could get to a point where you're all still trying to get the same one。

even though you have a permit。 Maybe not。 Maybe you could rewrite it that way。 But in this case。

let's not worry about that。 Let's only get grant permission again。

Once we're done eating and once we're done with everything。 So there might be a way to do it。

But yeah, in this case I would say let's not do that yet。

We could probably analyze it a little bit more and see but not, not at this point。

Let's just say just direct permission once you're done eating。

You certainly could do it before you do all that eating and so forth but I don't know。

that I'd do it before you sleep。 In that case, right?

It might not actually change the logic as it turns out but either way, somebody's going。

to be waiting around and they aren't going to get a permit until they aren't going to。

actually be able to eat until or do the locks until later。 Okay。 All right, so anyway, that's that。

Let's go write these other functions up here。 Let's go write wait for permission。 Actually。

let's write grant permission。 This is the easier one。

Grant permission is going to simply permits lock。lock because it has to, it's about to。

like do something to the permit。 So it's got to be the only thread that's doing that changing the permits。

And then basically it does permits plus plus because we're going to give back the permit。

so you'll see that wait permission decrements it。 Give back permits and then permits block unlock like that。

That's all that's doing。 Okay。 It's just basically incrementing the permits。 Now。

wait for permission is a little bit more involved because it's got to actually do the。

actual waiting。 Well, what does it have to do? All right。

The wait for permission basically we're going to wrap it in a while true loop。

Okay。 And it's going to check the permits。 Well, how do you check the permit? Well。

you'd better lock first。 Permits lock lock。

Okay。 And then if permits is greater than zero we're going to break。

We're going to be done。 That means that there is a permit available。 Let's go get it。 Okay。

And then we are going to do permits lock unlock。

Okay。 And then we are going to if we're still and then the reason we do this here by the way。

is we were not successful。 Like if permits was not greater than zero。 In other words。

permits was zero。 Well let somebody else try because we're not going to keep that lock for now。

We need because there needs to be people who can increment it。

Anybody who's eating now has to be able to use that lock later and increment it。

Does that make sense about why we're doing there? Okay。 And then after we do that, well。

if we just left this while loop as it was, this would, be a spinning loop that would be really bad。

It would really like hammer for every thread。 It would hammer a processor。

So let's at least do something at least somewhat nice sleep for let's say 10 milliseconds or。

something like that。 At least that throws it off the process for a second and does that。 Questions?

Any question? Do we determine the term of the theory? Not quite yet。 Okay。

We will do that after we check and make sure there's a permit available。 Oh。

that's not what this is doing。 It is。 But we haven't decremented that。 You'll see。 Okay。 Well。

think about what's happening here。 The final loop is basically saying, okay, look。

grab the lock so nobody else can check, whether do anything with permits。

Is permits greater than zero? If it's not, give back the lock。

sleep for a little bit and then try again。 That's what this loop is doing。

If the permits was greater than zero, then we go, great。 There's a permit available。

Let's break out of this loop so then we can go and do the deck of it。 You certainly could。

you could do it。 You could do it in here and then break if you want。 It doesn't really matter。

The point is that it's actually here that we're going to say permits minus minus。

And then because we broke before we unlocked here, we then have to do permits lock unlock。 Okay。

And that means that we're the ones who have taken the permit, decremented it and gone, with it。

Is that your question? Yeah。 That's, it's a little tricky。 And by the way。

I'm about to show you something where this weirdness where you have to unlock。

two places when you only lock once, we're going to get rid of that in a very, with a very。

cool class in a second。 Yeah。 So for the sleep for ten, if that's just。

what would happen if we could do that? Good question。 If we left out the sleep for ten。

the only thing that would happen is that we'd really。

be spinning like crazy and the processor would peg to 100% and it would, the fans would come。

on your computer and warm up and want to send the students wouldn't get their, their, their, their。

their, their, their, their computers done。 What's that? It would behave differently。

It would behave differently。 No。 But this is, this is called busy waiting。

not really spinning quite as much。 The reason it's busy waiting is because we are waking up every 10。

every 10 milliseconds, and going, is there a permit left? Oh, actually。

because there are permit left back to sleep。 And that's not the best way to do that。 In fact。

that's not really a good way of doing anything。 We kind of want just like, to suspend for processes。

we want the kernel to go or somebody, else。 In fact, not the kernel necessarily。 Nobody else to say。

go ahead and check now。 I've, I've let, I've released this so that you can check。

So it just completely sleeps until it gets a message that says, oh, I, now I could go。

look and hopefully it'll be ready。 Okay。 Ah, there are five philosophers。 Good question。

Who else is there? There are five different philosophers all vying for the permit。 Yes。

somebody else。 One of the other red。 You'll see。 Good question。 You'll see。 No, I know。 What, what。

we'll see。 And then notice what's going on down here。 We'll, we'll get to this later。 Okay。 Down。

down here, right? We are unlocking there。 Isn't this a great opportunity to tell all the other threads that are waiting?

Guess what? I just released one。 You can go grab a fork now。 All right。

So that's what we're going to end up doing。 We just have to figure out a way to do that。

And you actually need a little more support from the operating system to do that as it, turns out。

Okay。 What questions you have about this so far before we test it? No other questions? Okay。

Let's test it。 Anybody seen any bugs? Make, dining。

Now I'm all worried that I'm going to find a bug。 I can't fix again。 Oh well。 You can do it Chris。

Okay。 Make dining philosophers with busy waiting。 All right。 Oh, there we go。 There it is。 Oh。

let's see。 I, oh, thank you for telling me now。 Which one? In Maine。 All right。 Let's see。

Whoops。 Wait a minute。 You know what else we didn't do。

We actually didn't do philosophy yet because we have to do that。

I'm going to do it too。 So I'm glad it actually broke。 Which one did I spell wrong? Yeah。

I did that earlier when I took the, hang on a new, mute text。

I did that earlier when I did this too。 I think because I'm used to typing text instead of there。

There。

Thank you。 Thank you。 Good catch。 All right。 Let's go on and actually try this。

It would have been really ugly if it didn't work at all。 We just kind of didn't do anything。

Let's actually do our philosophers, a philosopher here。 Okay。 We're still going to do a forlip。

We're still going to think。 Okay。 But now we just have to pass in the other details。

That's the only other difference here is left, right。

And then we have to do the permits and then permits lock like that。 Okay。

Why don't we have to wrap around this one? Any ideas?

I guess my bigger question is why haven't we ever used ref before until one day ago in。

any C++ class you've ever taken?

They've always been what? Yeah。

They've always been in the right context。 Let's go look and see what EAT is expecting here。

EAT is expecting a permit reference, right? So you don't need to say ref around it because it's actually already expecting it。

If you did wrap it around it, it actually wouldn't matter but you don't need to in this。

case because it's not like the thread class which has no idea what these parameters are。

It's just going to take them and pass them along。 If you don't pass a ref that case。

it's just going to go, "I don't know which one you, want。", And that's that。

So in this case, we don't have to。 Just like we always haven't had to。 So that's that。 Okay。

I'll see if this works now。 Make dining philosophers。 There we go。 Okay。

Dining philosophers with busy way。 This is not going to look any different。

It's basically going to work them。

And if we put those, if we put that sleep in there, which we put, where did we put that。

in here? We put it right after here, right?

Sleep for 5,000。 Let's see what happens when we do this。

And then now it's going, everybody's going to start thinking and sleeping。

And now we're all sleeping, it should wake back up again。

There we go。 This can continue on because now we've got the right permits in there。

Okay。 Now I just artificially put all these weights in there, but that's how that's how it will。

go。 Okay。 All right。 What questions do you have about this going to continue for a little bit?

What other questions do you have about that? Anything else? Yeah。

We could explain why we need a lot of permits that hold you for a minute。 Good question。

Why do we need to see if we can find it in here? Why do we need to lock the permits before we decrement it or increment it here?

Yeah。 So let's say two threads end up stopping at exactly the same time, which would be possible。

and going and trying to release their permit, right? Well, couldn't you end up with。

they both did permits plus plus。 You could end up with a situation where the assembly code。

one of them increments it and, the other one is reading it。

but gets the wrong value and then increments it and there might。

be two increments or there might only be one increment instead of two。 It's actually。

it's a race condition there。 So you always want to wrap around your data structures that multiple threads could be。

modifying。 Does that help or is it still? Why? Yes。

If permits was zero and they both tried to increment it once, then you could end up with。

just permits being one instead of two because of that race conditioning, because of the。

assembly language like we talked the other day, it's not, plus plus is not an atomic operation。

In other words, you can't guarantee that inside there another one of the threads will。

end up reading, both will read one will update and the other one will also, you know, it'll。

be a little bit different。 It won't necessarily be the exact be correct。 Question。 Question。

Why do you have to lock when you're looking at permits? Good question。

What if someone else came in and decremented after you checked, then you would both decrement。

and then you'd be negative one permits and then all of a sudden you'd be in a deadlock。

condition again as well。 Right? So it's again, even if you're reading it。 Now。

there are times where you will be able to read from a data structure as long as you。

can guarantee no one else is writing to it。 That's fine。 Okay? This。

in things like maps and sets and things, you can read from some of those without worrying。

that someone else will be overstepping it。 It's a little more subtle than that。

but you'll get to some assignments where that's possible, but in this case it's not。 Okay。

Second question。 [inaudible], Why do you have to say rats instead of like hamper sand?

Right。 Good question。 Why do you have to say ref instead of ampersand?

And that's definitely bears repeating。

We do that right here。 Remember the thread class has this interesting function signature where it says I will take。

as many parameters as you want to give me。 And what I will do is then I will take all of the parameters after the first one and pass。

them into the first one as the parameters for that function when I start the thread。

That makes sense? Okay, when that happens, the thread has no idea if this function philosopher is going。

to take a reference or is going to take a value。 So when you pass the value into the thread。

it just goes, I don't know。 I suppose maybe you could go right a compiler that goes and looks at philosopher and goes。

oh, this one needs this, but it's not going to be, it's not necessarily going to work。

the way around。 So this is why I just say, look, make it a reference and there you go。 In fact。

I think if you didn't make it a reference, it would probably cause that same。

silhouette error we had the other day and you have to go fix it。 But then, you know。

the fix is to put that ref in there and say, look, it has to be a, reference。 Okay, good question。

Yeah。 So I thought you'd problem with the incrementing that is that either increment to much or。

that prevents too much? I mean, because they could happen to where they interpret what an increment together is。

one。 Yeah, so the question is, look, are you sure that the part of that, like I thought it was。

that the permits were you could either increment or decrement too much, but no, it's actually。

more likely that it's going to be too few。 Like they will both try to increment and you want it to increment。

Well, let's say two threads come in, they both increment, you could get two, right, which。

is what you want, but you could also just end up getting one increment because they both。

go back to last, go back to a Monday's lecture and take a look at the, take a look at the。

assembly code and see that, oh, look, if。 If this one reads the value before it updates。

then the other one reads the value and then, updates。

This one will update to the same value and not one more than it should be。 Go back and look at that。

You'll, you'll figure that once you go through the assembly code。

And then the permits dot lock makes sure to nothing like only one thing is access and。

then it blocks the rest of the accessing through that line。

Yes。 And this, this also bears repeating。 And we say permits dot lock。 Think about what's happening。

Many threads are all going to be at that same line at the same time。 Okay。 They're all maybe。

Let's assume they all go and try to run that line at the same time。

Only one of them will actually get the hold of the lock。

The rest will have to wait until that lock gets released。 So that's what it does。

It doesn't like tell anything to anybody else。 It basically says if everybody's trying to access that variable。

that lock, only one, will win, all the rest will just wait until that one releases it with an unlock。

That's the difference there。 Good question。 And that's critical to kind of get what lock is doing。

It's not like telling the colonel, hey, don't let anybody else touch this。 It's just。

it's much more simple than that as it turns out。 How is which? How is the waiting implemented? Oh。

how is the waiting implemented? Yeah。 Good question。 It's a, it's a nice sleep weight。 I mean。

it basically the, it's a, it's a, it's a nice sleep weight。 I mean, it basically the。

when lock unlocks, it will actually, it will actually set like。

a global variable more or less that everybody else reads。 And then one, and they do it atomically。

There are other instructions underlying assembly code instructions that are atomic。

And so the way it's built is on those instructions such that only one is successful as it comes。

out。 But it is a, it does wait until it doesn't do anything like spinning or anything。

Don't even worry about that。 Okay。 Now let's see a couple of different ways of doing this。

This was one way of doing this。 Okay。 Except that we still have this busy wait。

We still go to sleep for 10 milliseconds, wake up, go, hey, is there a permit available。

go back to sleep and so on。 And that's not really that great an idea。 Okay。

It's not the end of the world, but it's not a particularly good strategy。

In order to have this set up so that we can actually wait for the next, for the permits。

to become available, we have to use another language feature, which in this or another。

I guess another type of variable, which is going to be called a condition variable any。 Okay。

There's also just a condition variable。 You can use either。

We'll just use very condition variable any because it works with multiple different types, of locks。

But basically what it does is it allows you to say, okay, I'm about to wait for some lock。

to be ready。 Please put me to sleep until that happens。

So it's very analogous to six to spend in that case。 It's very analogous to that。 All right。

Here's how what it actually looks like。 A couple of details about it。 By the way。

this is not meant to scare you。 Jerry Kane thinks this is the hardest thing to understand in 110。

I'm not sure I agree。 Well, condition variable any take a little bit of like, oh。

and then you get it, right? It took me a little while, but I'll try to do my best explaining it。

And then you'll have to test it and see what happens。 But here's what happens。

You have a declare condition variable any and it has a weight function in it。 Okay。

It actually has two weight functions。 We'll talk about the second one in a minute。

but it takes a weight and it takes a new text, variable as the parameter for weight。

So you have some lock that you've locked。 Then you call this condition variable any weight function with that lock。

What it does is it puts the thread to sleep and then unlocks the lock until some other。

thread basically signals。 In fact, there's a notify to notify commands here。

The other threads notify the weight condition to wake up。 Okay。 So that's what's happening here。

There are actually two notifies。 Always use notify all as it turns out。

Notify one is a little wonky sometimes。 It doesn't matter。

Notify one would be if you know that there's only one thread waiting, you do notify one。

and that one thread is the only one that will get notification。 Or if you have multiple ones。

it will only notify one of the multiple ones waiting。

It's just better to write your logic to use notify all as it turns out。

That's not particularly important。 Just know that we're generally going to see notify all to say anybody waiting for this。

It's yours to try to get for the lock。 That's kind of what's going on。 Okay。 So to reiterate。

we have this condition variable, this condition variable any variable。 And you use it。

you declare the variable, everybody shares it by the way。 Okay。 Same thing like just like a mutex。

And then you pass in a lock that needs to be waited on。 It unlocks that, waits on it。

and then when it gets a signal, it relocks the lock if it, gets the lock and then goes on。

That's how it works。 Okay。 And you might be asking yourself, why is it unlocked?

We'll see why it has to unlock。 It doesn't unlock while it's waiting。 That things could happen。

It's just like why does SIGs suspend, unblock the signals when it's actually waiting because。

it kind of has to in order for the rest of the logic to work。 Okay。 All right。

So does that start to make sense about how that works? Yeah。 >> Is there a general question?

Is there a conditional variable of any or C++? They want something that can make the--。

Good question。 That's a very good question。 This conditional variable。

any is a C++ standard class as it turns out。 We will see one in a few minutes that's actually could be。

but they never built it, so we'll, use a different one。 You'll see。 I'll explain that in a minute。

But another thing。 But that's why it's your question。 Because yeah。

this one happens to be built for you。 And you do need it to be built for you because you need the kernel to help out with this。

waiting business。 Just like you did in SIGs suspend。

You're going to have a raised condition otherwise。 So that's that。 Okay。

Let's actually go and build this one。

Then dining philosophers with CV, wait one。

We will do two of these。 Oops。 That would be a CC。 Okay。

And what we're going to do here is it's basically going to be the same sort of thing。

I'm waiting on-- there we go。

The computer。 Basically the same idea as before。

We have to do a wait for permission, grant permission, et cetera。 Let's start with Maine。

Some of these are going to stay the same by the way。

We're not going to have to do any change-- make any change to eat or think。

We'll probably make philosopher changes just because of some variables that we have to。

pass in。 But that's that。 Okay。 Well, we might have to change the parameters a little bit as well。

In fact, I've already got it in here。

There's a condition variable any already in there。 I guess I left in。

But here's what we're going to do。 For Maine, again, same idea。

We're still going to have the permits。 Okay。 We're still going to have the mutex-- the mutexes for the actual forks in there。

We're going to have another mutex。 Just kind of like what we did before。

We called it permits lock before。 We're just going to call it m。

You will see this one a lot for mutex。 And we're just going to use that name for now。

And then we're going to have this condition variable any。 Condition variable any。

And we'll call that cv for condition variable。 Okay。 Same sort of thing here。

We're going to have a philosopher。 We're going to walk through each philosopher and start the threads。

We have to do the-- we have to start them off again。 Philosophers i equals thread。 In this case。

philosopher。 Philosophers the function, i ref, left, ref, right, ref permits again。

And in this case, we're going to have ref cv for the condition variable。

And we need that permits lock as well。 Because remember, wait takes a mutex itself that's。

going to use to actually do the unlocking and locking。 So we have to do ref m as well。 Thank you。

I'm missing prints on ref, right。 All right。 There we go。 Let's see。 Let's see if this one-- yes。

it looks right there。

Thank you。 Okay。 So that's that。 I don't believe there's anything else we have to do here。

We're still going to join same things before。

All right。 Let's go look at some of these other functions。

that we have to do。 Let's see。 Are we going to have to change-- oh。

I already changed each in this case。 So what we're going to do is we are going to do our wait for permission。

and our grab permission。 Now, this is where I'm going to show you a very cool new way。

to use a lock around a variable。 It is called a lock guard。 Here's how it's going to work。

Lock guard-- it goes like this, mutex, because it takes a parameter like that。

Lock guard m。 And here's what this does。 This is why it's such a cool class。

It's the easiest class in the world。 All it has is a constructor and a destructor。

In the constructor, the only thing it does, is calls lock on that mutex。

When you create this-- so at this point, after this line, the mutex has been locked。

The only thing the destructor does is call m。unlock。

What's nice about classes like this-- and in C++, this is true--。

whenever a variable goes out of scope, what happens? The destructor gets called。

So we don't need to worry about unlocking this。 As long as we want to unlock as we leave。

we just say, well, it's out of scope, it's going to get unlocked。

It's the cleverest little class I've ever seen because of that。 Now。

what we're going to do here is we are going to do a while loop, permits equals 0。

And then we're going to do cv。weight。

And then we're going to wait on the lock that we already just locked。

And then if at any time we get notified that the permits has, actually gone above 0。

as it turns out--, whenever we get notified, we will check again to see if it's still 0。

then we will do that。 But after the cv。weight, it unlocks so that it can go back。

and do the check again--, I'm sorry, relax。 So it can do the check again so that you。

don't have two different threads trying to do that。 I'll go over that again。 And then after here。

we know that at this point, we have gotten to a place where permits equals 0。

We have the lack still。 So we know that we can do permits。 Permits is not equal to 0 at this point。

I should say。 We could do permits minus, minus, like that。 OK。

At what point does the lock unlock for reals when it after 62? After we leave the function。

it's out of scope。 The destructor gets called。 The lock gets unlocked。 OK。

So let's walk through it one more time。 This is definitely challenging。

We lock the lock using this lock guard so we know it's, going to get unlocked。 We now hold it。 Now。

many threads could be contending on this, but one of them is going to get it。

So assume that our thread is the one that gets it。 Then we go down and we say, oh, if permits is 0。

then we'd, better wait because there are no permits available。 What happens is we tell wait。

use my mutex variable to wait on, and unlock it after you push me off the processor。

It unlocks the mutex and then sits around waiting for, to be notified。 When it gets notified。

it reacquives that lock if it can, and then it goes and checks and then comes back up to the top。

of the while loop again and checks to see if permits is, 0 again。 At this point。

hopefully it is not。 In fact, it wouldn't be if we acquired the lock and we got, notified。

then it wouldn't be。 And then it would get out of the while loop。

Deck my permits because we are now holding one。 Yes?

So it's waiting to be told that it isn't checked again or it's。

waiting to be told that it's re-locked? It's waiting for whatever who ever notifies it。 Now。

let's see who notifies it。 Let's go on to the next thing you notice。 Somebody else had a question?

Yeah? [INAUDIBLE], Oh, yeah。 If you unlock it, good question。 If you unlock it。

it's not even guaranteed that the same, thread will get the lock again。 The CV wait tries to unlock。

If it can't unlock it, it just goes back to sleep until it, gets another notification。

Because it tried and it can't do it, so that's that。 It tries to-- sorry, it tries to re-lock。 Yes。

thank you。 It tries to re-lock。 If it can't, it just goes, oh, I'll wait around again。

I'll keep waiting until I get another notification。 Let's see who notifies it。 Well。

there's only one other thing that really could, notify it in this case。 It's grant permission。

Let's see how that works。 Grant permission is also going to use a lock guard for a。

mutax, LGM。 And now all lock guard-- remember all lock guard need, to do before was update permits。

Permits plus plus, because it's releasing the lock, or the, permit, that it just finished the。

philosopher just finished eating。 Therefore, it's going to release this, and we're good to, go。

Well, now, once it's released it, what does it need to do? Well, it needs to tell everybody else。

hey, guess what? There is a permit there。 Now, what is the only case where it would matter if we。

have four permits? One。 One。 That's the only one that would matter, right? So if permits equals one。

because we're the ones who just, made it go from 0 to 1, meaning there's going to be。

someone possibly waiting for that, then we might as well, notify--, oops, our CV。 I should say CV。

notify。 And that will send a notification to the other。

threads that are waiting for them all to try to wake up and, re-acquire that lock。

And when they re-acquire that lock, then the one that does, gets the permit and moves on。 Question。

Is that only for clarity, or would something break if we, didn't actually keep it up? Like, hey。

if we didn't make it, only a permit, couple is more。 Oh, is it only for clarity? Like, you know。

if you just said, CV notify after you do, that, it probably wouldn't matter, actually, because。

they're all checking for 0 anyway。 So if there's one available, then-- yeah, I don't think。

that would-- I think that would--, it's not necessarily just for clarity, it's just for--。

let's not send more notification than we need to, either。

There's definitely not going to be any worry if there's two, or more than one at that point。

I was already feel about this one at this point。

Let's try it。 Anybody see any bugs?

Make dining philosophers with CV weight 1。

All right, dining philosophers with CV weight 1, and there we, go, and it should, or there they go。

continue, continue, continue until they all do it。

So that's how that works。 Question。 How does it maybe a very helpful, how did you spread it。

there? How does CV know how many threads are there? It has no idea。 Does it need to?

It doesn't really need to。 Your logic is what kind of dictates when these things--。

we do know that permits is what we're looking for。 We're looking for four permits。

There's four permits available。 If four dining philosophers already have those permits。

we are not going to let another dining philosopher even, pick up for the first fork。

That's what's happening there。 And we are using this idea that we。

are trying when we first go into lift up the left fork, we're going in and saying。

get us permission first。

And that's where the weight permissions comes into play。

which says, OK, everybody's going to do this first。 They are going to try to acquire the lock。

If they acquire it, they are going to check and see, if there are permits available。

If there are not permits available, then they are going to wait for a permit to be available。

When one becomes available, they're, going to try to reapply that lock。

and then decrement permits such that they actually hold one。 One second。 So notify all。

How does it know? Oh, notify all。 It's like some global variable that everybody's looking at。

basically。 I mean, I think the kernel has some role in this as well。

as a thread manager or whatever。 But it's built in such that when you say notify all。

anybody who happens to be waiting has, registered to get that notification when it happens。

Why didn't we use the lambda function one? We will。 We're going to see a different one in a second。

Because this while loop is here。

this is a really common way of doing this。 You can't really say if permits equals equals zero。

because there is going to be some sort of a race condition。

in there to actually re-acquire that lack later。 So this while some condition is true, wait on it。

That's so common that they've built it, into the condition variable any。

OK? You didn't have to the while loop, that you used for about a week。

If you didn't have the while loop。

you will。 Yeah, let's see what that lambda function。

that you're talking about looks like in a second。 So let's see。

We talked about all of this with the acquiring lock。 OK。 Like I said。

because this is such a common thing to do。

we have it built in such that it looks like this。 It's actually a templated function。

which basically means that you don't know exactly what, the type is going to be, but that's OK。

Here's what you do。 Instead of doing a while loop, you。

let the wait statement kind of do that while loop for you, by passing in some other function that。

makes the decision and returns true or false based, on that decision or based on that question。

So in this case, you would say-- and this is exactly what, it's doing in here--, is that while loop。

It's just doing it for you。 So what could this predicate be? Well。

it could be a function that checks to see if permits is, zero or not。 That's what it is。

You can't just pass in the value permits equals zero, because you need to check it every time。

and it could change。 So if you try to pass it in directly as that, you could。 So what do you do?

You pass it in as a function or, since we're in C++, as a lambda function。

which is the way we can actually do this, here, let's think about what this is actually doing。

You would say-- and this is the same function we had before--, Cv, wait, there's your mutex lock。

which remember it's going to unlock and then, reacquire it later-- you are going to say。

give me access, in here to the reference of the permits variable in that, function。

And in that function, you are going to check and see if, permits is greater than zero。

and return true if it's greater, than zero。 That's basically the exact same check you were doing before。

It's the inverse of the check if it's equal equal zero。 Right there。 [INAUDIBLE]。

The lambda that you pass has to be a Boolean return。

Because it's asking-- it has to know when to get out of that。

Remember, here's how it's built。 Right? While that, which is true or false。 So that's that。

It could return something that can be converted into a, true or false。

but that has to be true or false in that sense。 So that's how you use that。

and then you don't need to。

worry about even the while loop。 OK, so this is why these CV variables are relatively common。

So we can go one step further。 This idea that maybe we're waiting for a whole bunch of, threads。

some concrete maximum number of them can do, something。

we could use the CV with this permits variable。 Why not wrap it into another type of data structure or class。

that actually allows us to do this for any number of, various site permits, et cetera。

This is what we call a semaphore, which is basically, this whole idea of I have x number of threads。

and I have, only some of those allowed to do something at a given time。 In our case。

it's pickup forks, which seems a little silly。 In a real life scenario。

one you're going to code up for, the next assignment, you're going to be querying the, internet。

and you're going to be doing it in many threads。 And what we want to do is we want to limit the number of。

threads that are actually accessing a, particular web page。 I don't know if you know that--。

have you ever heard of a DDOS, a distributed denial of, service attack?

Basically means that you have a website and it's able to。

take a bunch of connections for people who want the web, pages。

And what happens is various nefarious people get lots of。

servers that try to hammer away at asking for requests from, that web server。

and it can't field them all。 And so people who aren't nefarious are trying to access, it。

and it basically brings the website down, where it makes, it really hard to access it quickly。

You don't want to do that if you're nice。 So you want to limit the number of threads that can access a。

particular website at a given time。 We're going to do it somewhat artificially, but you want。

to be able to do that。 So you want to be nice, and many times you want to limit the。

number of things that can happen at a particular time。 This is how you'll do it。

So you might have 100 threads ready to go to that website, asking for something, but you say, no。

no, let's only let, them do it at a time so that we're nice so other people can。

get in there and do that。 That's what's happening。 And this is a very common sort of idea。

And in this case, we are going to use a thing called a, semaphore to do this。

Your question came up earlier is, are condition variable, entities built into C++? Yes。

they're a library。 The bigger question is, why isn't a semaphore built into, the library? Who knows?

Turns out it's really easy to build one, because all we're。

doing is really wrapping around that variable that's the, number of kind of count we have。

Why didn't they just build it in there? Who knows?

Maybe the next some other version of C++ will have that in, there, but they don't have it。

But let's actually see how it's built。

The semaphore constructor, it says the semaphore, instructor is so short that it's in line。

Let me actually show you this。 C-D/user/class CS110。

Local include, I had to look up what this was。 And then semaphore。h, here we go。

Here's the class itself。

There's the class definition right there。 And notice that constructor has an integer as the value。

and, it just either sets it to zero, or it sets it to whatever。

value number you pass in in the constructor。 So if you say I want five dining philosophers。

you would, say semaphore parentheses five, and it just sets that, variable to five。

Just like we did with permits equals, or I guess semaphore, equals four, let's say。

permits equals four。 It's exactly what that's doing here。

That's all the semaphore needs to do for this case。

All right。 And then there is a weight function as part of the semaphore。

It's very simple。 It's exactly what we've already kind of seen here。

It basically puts a lock around the mutex that it's got。

And then it calls the weight function to try to wait for it。

based on the value being greater than zero。 And the value being greater than zero is the number of。

permits, if you will, that we have。 If that number is greater than zero, then it。

decrements it and ends up making it so that it can keep, track of those permits。

So it's really a very simple wrapper around a condition。

variable, any, that does all the stuff we wanted to do。 Again, it's because we do this so often。

That's how the mutex works, like that。 At the end when you are done with your permit, all you need。

to do is call signal。 You don't need to worry about whether or not the permits are。

greater than or equal to zero。 It just does exactly that。

It does what we did before in the actual class。 It increments it。

We'll talk about another interesting use of this, by, the way。

as we'll see this kind of next week when we get on, there。 But in this case。

let's see how we might modify our dining。

philosophers in this case。 Well, we can say semaphore permits。

We can say semaphore permits of however many we want。

No more need for a condition variable, any。 It's already built into the semaphore。

Meaning that all we need to do now is pass in a reference, to that semaphore as the permits。

And everything else stays the same, except now we don't need。

to call a particular function on this。 We've got the semaphore, which is doing all of that like。

checking for us。 So we just say permits。wait。 What does that mean?

It will wait until there are at least one permit available。 And that's where we're doing。

If you want to, when you're done with your permit, you call, permits。signal。 So it's all that stuff。

We just talk about it, simplifies it。 And it says, oh, now we've got this thing called a semaphore。

which says you have x number of things that can do it at a, time。

and maybe x plus y number of things that want it。 Wait until there are at least one available to do that。

And we'll--, [INAUDIBLE], When can't you use semaphore? When is it better to use that?

You will see a couple examples in lab next week。 No labs this week。

You'll see a couple examples in lab。 You can actually put the week after where you'll see this。

C of any of the-- oh, now I see why that might be important, to use that。 But better generally。

we're going to almost always use--, you will probably need one for a future assignment where。

you're not really waiting for a whole bunch of things。 And by the way。

a mutex is just a semaphore with a value of 1。 Because a mutex is going to lock or unlock for one thing。

That's really all it is。 So mutex is like a special version of semaphore for only, one thing。

But yeah, we'll see some more examples where you will, probably need a condition variable, any。

But in general, most of the time, you'll be able to use。

semaphore for this。 All right, I think we should probably stop there。

You guys have mid-term coming up。 You have other things。 And that's that。

Are there any last-minute questions? I'm going to also go back from here, back to my office, for。

another hour and a half or so, for office hours, if you want, to come back。 All right。

we'll see you tomorrow for the midterm。