Module three: Threads in Go. Topic one point one: Goroutines. So, in the previous modules we've been talking about concurrency and parallelism, and how processes work and threads work, how they execute concurrently at least and maybe in parallel if you have extra hardware. But we haven't really talked about how you actually implement that and certainly not in Go, but how you implement that in software. So, in order to get these multiple threads, we specifically focusing on threads. In order to create these threads of execution, you have to use certain programming constructs that are built into the Go language and this is one of the advantages of Go. That it has a lot of these constructs built right in, and they're easy to use. So, say you want to create a Goroutine, right? Which is a thread that can run concurrently with the other Goroutines. Now one Goroutine is always created automatically to execute the main. So, even if you don't do anything special you run a main, it will create a Goroutine execute the main inside that Goroutine. Other Goroutines are created using a Go keyword. So, as an example down here on the left, we have the on the left we have an example where there's just the one Goroutine main. Okay, this is some segment of main, right? A equals one you call foo equals two. Let's just make this one Goroutine the main Goroutine executing all three of those instruction the fact that foo is the function call. Now, on the right is the version where we actually create a new Goroutine. Just to execute that function foo. So, you can see on the right there's a equals one that's executing the main Goroutine. Then we say, go foo that creates a new Goroutine which will execute the code in foo, and then we say a equals two. Again is still in the main Goroutine. So, after you call gofoo there are now two Goroutines. The main Goroutine and this newly created this call subroutine, sub Goroutine that's created. So, and notice it on the right. So, on the left the main will block when it comes to foo. When I say block, the main will, so there's a equals one then foo then equals two. A equals two can't execute on the left until foo is complete. So, foo well it's non-blocking but foo has to completely execute before a two can happen and this is on the left. On the right where you have the extra Goroutine that you've created you say, a equals one in the main then go foo, it creates this new thread or new Goroutine to execute foo and then a equal to can execute while foo is going on, or before foods even started. We don't know the timing it depends on the scheduler,on the runtime Go runtime scheduling. How that's going to be scheduled but a equals two does not, it can execute immediately. It doesn't have to wait for foo to complete or anything like that because foo is being executed in a in a concurrent Goroutine. So, I just showed you how to start a Goroutine it is pretty simple you say go, and then give the name of the function that you want to execute, or define the function you want to execute and the Goroutine will execute the code associated with that function. A Goroutine generally exits when its code is complete. Okay. Whenever it's done with whatever function is executing it's done it returns from that then the Goroutine just exits. Now, when the main Go routines complete all other Go routines are forced to exit. So, that means, if you start with the main you can make several Goroutines from the main, but if even if these other Goroutines are not finished with their code, if the main Goroutine ends, then all the other Goroutines will be forced to end and will be forced to exit. This is important to understand, to think about when you're writing your routines because what'll happen is, the Goroutine that you create may not complete its execution because the main completes first. So, you might get this behavior where you say, "Look I wrote this code and this code to execute and it doesn't seem like it's executing." That might be because the main is completing before the Goroutine ever got to finish its own code. So that's something to watch out for. Thank you. Module three: Threads in Go. Topic one point: Exiting Goroutines. So, we already described how Goroutines will exit when they complete their code, but also Goroutines will be forced to exit if the main Goroutine exits early. Then the all the Goroutines that are created they all are forced to exit. So, they might exit early before they do their job, right? Before they finish the job. So, I'll give a little example a piece of code here. By the way in these code segments, I'm generally clipping out the importing the packages, right? In order to use this program I have a print f right and I'm using the fmt package so I haven't put the part in there where I import that. But assume that's there I just want to get right to the Goroutines. So, in this main is pretty simple, first thing it does is it creates a new Goroutine which had just exit just do a printf, rather and print a new routine and then after that or not we don't know the order but at some other point in the main Goroutine format.print.f, main routine. So, you expect to see two things printed on the screen, main routine and new routine and you wouldn't necessarily know what order these two are going to get printed in. Right? Because when the Goroutine is created, we don't know how the scheduler is going to schedule the main routine in a Goroutine. Maybe it schedules the Goroutine first the new Goroutine first maybe it schedules the main Goroutine first, we can't really tell. But at a glance, you would expect it to print main routine and print new routine at some point in some order. But when I execute this, only main routine is printed. Why does this happen? Is because the main Goroutine is finishing before the other new Goroutine starts. Okay. This happens consistently. So, theoretically we don't know how the schedule is going to work. The Go runtime scheduler. We don't know is it going to let the main Goroutine run first, or the new Goroutine run first we don't know. Theoretically, if you ran this over and over, you could get different ordering, right? You would think well, sometimes the main goes first, so it does main routine and then it quits. Sometimes it does the new Goroutine then prints a new routine and main routine. Right? Maybe. Actually what happens is, when I run this anyway, every time it just prints main routine and quits. So, what I presume from this is, what I assume from this rather is that, the scheduler, the Go runtime scheduler is giving preference to the main routine. So it's always lighting the main routine go first before the new Goroutine. I don't know that for a fact, but it seems like that's how it's implemented. But even if that's how it's implemented we have no definite knowledge of that. Meaning, maybe it's implemented in that way right now, but then in the next version of Go, they might change the scheduler so it does something different. Okay.? So, that's not something that ordering, we can rely on. We have to assume that we have no idea what order the scheduler is going to work in, what order is going to execute these Goroutines in. So, definitely- but definitely though what's happening here is not what I wanted, right? It just print out main routine never print out new routine. So, what- so I got to get this this main so that it doesn't exit so early. Right? I don't need- I don't want main to somehow wait for the other Goroutine to execute- exit before, it so that I can get the new routine to be printed out, right? So, how should I do this? First, I'm going to show you this one way which is really a hack, but I'm going to show it to you anyway. Okay. Just so you know what not to do. But it works sometimes but it's not a safe thing to do. So, here's what I do. What I do is I just add this line that I highlighted in red, time.sleep and I give it 100 milliseconds. So, what I'm doing is I'm and notice that that time that sleep, it executes in the main Goroutine. Not in the new Goroutine that I created in the main Goroutine. I create the new Goroutine call and go, then I put the main Goroutine to sleep for a little while, 100 milliseconds and then I have the main Goroutine print main routine. Now what that does is, in that time that the main goal routine is sleeping, the Go runtime scheduler isn't stupid, it's not going to just let the machine sit like that when it knows it has another Goroutine available. Right? Because remember a big benefit of doing concurrent programming is that when one routine, one Goroutine or one thread is waiting is blocked and waiting for something, then the scheduler can just move in another one. So, it doesn't have to waste time it could say look, I've got another available Goroutine let me schedule that. So that's exactly what's happening here. When you put the main Goroutine to sleep, the main can't run so the scheduler, the Goruntime scheduler schedules in the the new Goroutine that it created that i created just before it prints out new routine, and then main routine printed out. So, what I get printed now is new routine, main routine just like that with no space because I didn't put a space or carriage return or anything. But that's what I get. So, this is my hacky attempt to get the Goroutine, the main Goroutine to stay alive long enough for the new Goroutine to actually complete its execution. It works. It works pretty consistently on my machine. But this is not what you should do, but this this worked you can see how this worked. To do what I did adding a delay like that to wait for Goroutine is bad, bad, bad. Reason why is because you're making assumptions about the timing and your timing assumptions may be wrong. So, in this case,, the big assumption I'm making is that the delay of 100 milliseconds which I injected in there in the main Goroutine, I assumed that that will be enough to ensure that the new Go routine has time to execute. So, I assumed look if- I assumed that certain behavior of the scheduler to. I said, look if I'm going to- if I force the main to delay for a 100 milliseconds, the Go runtime scheduler of course it's going to schedule this other Goroutine that I've created. I assume that. I don't know that. I don't know how they've implemented that but I assume that. So, I assume that the Go runtime scheduler is going to schedule a particular Goroutine that I had in my mind. Also, I'm assuming something about the operating system too because for all I know, the operating system which is running above the Go runtime. It could take that 100 milliseconds and move in another thread entirely. It might just- so right now I've got- if you remember it there's a thread in OS, an operating system thread, that all the Go code is running in. Right, all the Go threads are running within this one operating system thread. The operating system might just say look, I'm going to take that thread and swap it put it out of context switch it and bring it in an entirely different thread and maybe it'll bring in PowerPoint or something which is running on this machine. Right! So that it might use that 100 milliseconds in a way that I don't anticipate. Now, it doesn't. There are reasons why it doesn't, but I'm making certain assumptions about how the operating system scheduler works and about how the Go runtime scheduler works and these assumptions are not safe to make. Right? I have to- because they could these can change in different version of the operating system. So, relying on timing like this, is not safe. The timing is non-deterministic meaning every time you run the program, the timing might be different, and you have to assume that. So, because if you don't, what will happen is at some point you get these really bad intermittent errors. Where most of the time it'll work, but sometimes that you're timing assumption will be violated and sometimes the error will happen. These are the worst- these are one of the worst types of errors because then you get these intermittent errors which the only happened occasionally. So, you run it and you say I found a bug let me run it again to reproduce it so I can debug it and you can't. Right, you run it again and again the bug doesn't happen again. Those are terrible types of errors to have to debug. So, this is not what you want to do. You don't want to rely on timing.l have a long story which I could talk about where I rely on timing as an undergrad and failed badly in a big assignment. I won't bring that up is interesting but it's sort of attract. Still you can't rely on the timing so you need formal synchronization constructs and we'll talk about those. Thank you