1 00:00:06,640 --> 00:00:08,440 - So now that we've reviewed 2 00:00:08,440 --> 00:00:10,850 how the operating system schedules work, 3 00:00:10,850 --> 00:00:13,560 and the Go schedules work, and how it sits on top. 4 00:00:13,560 --> 00:00:15,860 We now want to start learning how we, 5 00:00:15,860 --> 00:00:18,260 manage concurrency in Go. 6 00:00:18,260 --> 00:00:20,880 But I want you to understand something here, 7 00:00:20,880 --> 00:00:23,730 Go routines are chaos. 8 00:00:23,730 --> 00:00:25,840 Look, I've got five kids. 9 00:00:25,840 --> 00:00:27,680 When I tell people I have five kids, 10 00:00:27,680 --> 00:00:30,800 usually what happens is the person bends over and goes, 11 00:00:30,800 --> 00:00:33,590 oh my god, oh my god, you've got five kids? 12 00:00:33,590 --> 00:00:35,920 I've got one, how do you do that? 13 00:00:35,920 --> 00:00:38,340 And I always say, I just travel a lot and get out of town. 14 00:00:38,340 --> 00:00:40,470 Right, I just get out of town. 15 00:00:40,470 --> 00:00:42,880 Well you can't get out of town, 16 00:00:42,880 --> 00:00:44,920 when you're dealing with managing concurrency 17 00:00:44,920 --> 00:00:46,590 and multi-threaded software in Go. 18 00:00:46,590 --> 00:00:49,525 You're in town, you're in the house. 19 00:00:49,525 --> 00:00:50,630 You've got to deal with the chaos. 20 00:00:50,630 --> 00:00:54,113 I always like to think of Go routines as children. 21 00:00:55,510 --> 00:00:57,040 If you don't have children yourself, 22 00:00:57,040 --> 00:00:59,280 I'm sure you've got nieces and nephews, right? 23 00:00:59,280 --> 00:01:01,250 And you know that children are chaos. 24 00:01:01,250 --> 00:01:02,510 Like, one's running out to the pool, 25 00:01:02,510 --> 00:01:03,440 one's running up the stairs, 26 00:01:03,440 --> 00:01:04,510 one's running across the street, 27 00:01:04,510 --> 00:01:06,080 one's about to pull off their diaper. 28 00:01:06,080 --> 00:01:07,670 Which one are you going after? 29 00:01:07,670 --> 00:01:09,140 Like, I'm going after the diaper, 30 00:01:09,140 --> 00:01:11,480 man, I know that is a big mess. 31 00:01:11,480 --> 00:01:13,570 But the reality is you only get one. 32 00:01:13,570 --> 00:01:15,980 You only get to go after one of those kids, 33 00:01:15,980 --> 00:01:18,770 when they're running in parallel, right, 34 00:01:18,770 --> 00:01:20,330 and there is only one of you. 35 00:01:20,330 --> 00:01:22,990 So, really what we're gonna be talking about, 36 00:01:22,990 --> 00:01:24,110 for the rest of this section, 37 00:01:24,110 --> 00:01:26,850 is really, what I always say, a parenting class. 38 00:01:26,850 --> 00:01:30,230 I've gotta teach you how to manage your children. 39 00:01:30,230 --> 00:01:32,860 Every time you bring a Go routine into your program, 40 00:01:32,860 --> 00:01:35,330 you're bringing a child into this world. 41 00:01:35,330 --> 00:01:36,887 This is now parenting 101. 42 00:01:38,470 --> 00:01:41,533 I've got to teach you how to keep these children safe, 43 00:01:41,533 --> 00:01:42,366 I've got to teach you how to keep 44 00:01:42,366 --> 00:01:43,663 the house from burning down, 45 00:01:44,568 --> 00:01:47,540 because multi-threaded software development is complex. 46 00:01:47,540 --> 00:01:49,450 Now I told you there's two aspects to this, 47 00:01:49,450 --> 00:01:52,970 there is synchronization and there is orchestration, 48 00:01:52,970 --> 00:01:55,930 and there's tons of rules around managing concurrency. 49 00:01:55,930 --> 00:01:58,620 Like this one, you're not allowed to create 50 00:01:58,620 --> 00:02:00,710 a Go routine, or bring a child into this world, 51 00:02:00,710 --> 00:02:03,200 unless you know how and when. 52 00:02:03,200 --> 00:02:04,890 Right, you're not allowed to create a Go routine, 53 00:02:04,890 --> 00:02:06,010 unless you can tell me how and when 54 00:02:06,010 --> 00:02:08,750 that Go routine is going to be terminated, 55 00:02:08,750 --> 00:02:09,940 before the Go program shuts down. 56 00:02:09,940 --> 00:02:11,230 My equivalent to that is, 57 00:02:11,230 --> 00:02:13,790 you're not allowed to lock the house up at night, 58 00:02:13,790 --> 00:02:15,470 until you know that those Go routines, 59 00:02:15,470 --> 00:02:18,640 those children, are safely tucked away in their beds. 60 00:02:18,640 --> 00:02:19,920 You've gotta be managing this. 61 00:02:19,920 --> 00:02:23,730 You gotta have known control over anything that's going. 62 00:02:23,730 --> 00:02:25,650 Synchronization, orchestration. 63 00:02:25,650 --> 00:02:27,050 So what I'm gonna show you right now 64 00:02:27,050 --> 00:02:30,740 is some basic orchestration using a weight group. 65 00:02:30,740 --> 00:02:32,850 And to show you how we are going to like, 66 00:02:32,850 --> 00:02:35,420 keep our programs running until we know that 67 00:02:35,420 --> 00:02:38,470 the Go routines, or these paths of execution, are done. 68 00:02:38,470 --> 00:02:42,339 This is basic orchestration using weight groups. 69 00:02:42,339 --> 00:02:44,540 We'll also learn how to create Go routines. 70 00:02:44,540 --> 00:02:47,660 After this though, we've got to learn about data races. 71 00:02:47,660 --> 00:02:49,743 We've got to start learning about 72 00:02:49,743 --> 00:02:51,040 synchronization and eventually orchestration, 73 00:02:51,040 --> 00:02:52,810 because without it, 74 00:02:52,810 --> 00:02:55,684 you're going to have some really nasty code, 75 00:02:55,684 --> 00:02:58,273 complexity, bugs. (groans) 76 00:03:00,010 --> 00:03:03,050 But here we are, let me do my best here to 77 00:03:03,050 --> 00:03:05,290 try to get you where you need to be 78 00:03:05,290 --> 00:03:09,000 so you can start really being successful here, 79 00:03:09,000 --> 00:03:12,393 with a less is more, less complexity is more attitude. 80 00:03:13,330 --> 00:03:15,500 So let's start with this program right here. 81 00:03:15,500 --> 00:03:19,370 Now I, on my Mac here, I have an eight core machine 82 00:03:20,833 --> 00:03:23,660 and I wanna run some of this code as single-threaded. 83 00:03:23,660 --> 00:03:25,930 So what you're seeing on line 17, 84 00:03:25,930 --> 00:03:29,000 is me calling the GOMAXPROCS function, 85 00:03:29,000 --> 00:03:30,580 from the run-time package, 86 00:03:30,580 --> 00:03:32,250 telling it to knock my Ps 87 00:03:32,250 --> 00:03:34,380 from eight to one, single-threaded. 88 00:03:34,380 --> 00:03:35,950 And it's capitalized because 89 00:03:35,950 --> 00:03:39,100 it represents an environmental variable 90 00:03:39,100 --> 00:03:40,570 that this can override. 91 00:03:40,570 --> 00:03:43,290 We used to have to use this back in the day because, 92 00:03:43,290 --> 00:03:46,639 prior to 1.5, your Go program only came up single-threaded, 93 00:03:46,639 --> 00:03:49,660 or one P, regardless of the number of cores. 94 00:03:49,660 --> 00:03:53,806 From 1.5 since, we match Ps to cores 95 00:03:53,806 --> 00:03:56,163 that are identified on the host machine. 96 00:03:57,240 --> 00:04:00,320 Okay, now what you're seeing next to line 23 97 00:04:00,320 --> 00:04:03,330 in the main function, and again I apologize. 98 00:04:03,330 --> 00:04:04,620 This is being done in a net, 99 00:04:04,620 --> 00:04:06,530 so it happens before main. 100 00:04:06,530 --> 00:04:08,760 In the main function here, on line 23, 101 00:04:08,760 --> 00:04:11,010 what you see is the weight group. 102 00:04:11,010 --> 00:04:15,226 In fact, we are creating a value of type weight groups, 103 00:04:15,226 --> 00:04:19,380 set to its zero value, naming it the variable WG. 104 00:04:19,380 --> 00:04:22,950 And we're calling wg.Add(2). 105 00:04:22,950 --> 00:04:25,010 I don't want to see you calling Add(1), 106 00:04:25,010 --> 00:04:26,970 Add(1), Add(1), , Add(1). 107 00:04:26,970 --> 00:04:28,670 In other words, the weight group is a 108 00:04:28,670 --> 00:04:31,163 synchronous counting semaphore. 109 00:04:32,039 --> 00:04:33,390 We're gonna keep a count of all 110 00:04:33,390 --> 00:04:35,930 the existing Go routines that we create. 111 00:04:35,930 --> 00:04:37,980 So we know that we're not going to shut down 112 00:04:37,980 --> 00:04:41,290 until that count goes back down to zero. 113 00:04:41,290 --> 00:04:43,810 So I want to do Add one time 114 00:04:43,810 --> 00:04:46,070 for the number of Go routines we're gonna create. 115 00:04:46,070 --> 00:04:47,762 If you don't know how many Go routines you're gonna create, 116 00:04:47,762 --> 00:04:49,470 stop, you're going down a very bad path 117 00:04:49,470 --> 00:04:52,520 and we're not gonna call Add(1) as they get created. 118 00:04:52,520 --> 00:04:55,000 We call Add one time, upfront. 119 00:04:55,000 --> 00:04:55,833 There it is. 120 00:04:55,833 --> 00:04:57,140 So now we know we're gonna be creating 121 00:04:57,140 --> 00:05:01,040 two new Go routines in this program 122 00:05:01,040 --> 00:05:03,690 and I don't want this program to end until 123 00:05:03,690 --> 00:05:05,735 those Go routines are finished doing their work. 124 00:05:05,735 --> 00:05:08,450 And this is where we get the wait call on line 42. 125 00:05:08,450 --> 00:05:10,920 The wait call is a blocking call. 126 00:05:10,920 --> 00:05:13,220 A weight group has three API's, 127 00:05:13,220 --> 00:05:16,300 add, wait, and done. 128 00:05:16,300 --> 00:05:19,240 Add is adding to the counting semaphore, 129 00:05:19,240 --> 00:05:22,520 done is decrementing, and wait will block until 130 00:05:22,520 --> 00:05:25,830 the count goes from two to zero. 131 00:05:25,830 --> 00:05:27,830 Again, we don't want main returning 132 00:05:28,729 --> 00:05:31,200 until we know those other Go routines are done. 133 00:05:31,200 --> 00:05:34,710 What you're seeing now on line 29 and 35 134 00:05:34,710 --> 00:05:38,300 is the declaration of a literal function, 135 00:05:38,300 --> 00:05:41,120 then the execution of that literal function, 136 00:05:41,120 --> 00:05:43,200 remember literal means unnamed, 137 00:05:43,200 --> 00:05:44,880 these are unnamed functions, 138 00:05:44,880 --> 00:05:47,470 and then the use of the keyword go 139 00:05:47,470 --> 00:05:49,232 in front of the function call. 140 00:05:49,232 --> 00:05:51,000 This is essentially going to define, 141 00:05:51,000 --> 00:05:55,120 call, and then execute, this function as a Go routine. 142 00:05:55,120 --> 00:05:57,470 A separate path of execution. 143 00:05:57,470 --> 00:05:59,880 I also want you to notice we're using closures. 144 00:05:59,880 --> 00:06:02,900 I like closures, I think it helps simplify 145 00:06:02,900 --> 00:06:05,610 how these literal functions access data. 146 00:06:05,610 --> 00:06:06,670 You can pass it in, 147 00:06:06,670 --> 00:06:08,740 we're gonna have to do that at some point today. 148 00:06:08,740 --> 00:06:12,410 Closure bugs can be nasty, but the golint tool, 149 00:06:12,410 --> 00:06:13,410 which you should be using, 150 00:06:13,410 --> 00:06:17,600 can detect closure bugs, and you'll be okay. 151 00:06:17,600 --> 00:06:21,900 So what these two Go routines do is call a named function 152 00:06:21,900 --> 00:06:24,410 called lowercase and uppercase. 153 00:06:24,410 --> 00:06:26,600 These functions aren't doing anything special. 154 00:06:26,600 --> 00:06:28,580 They're just displaying the alphabet in lowercase 155 00:06:28,580 --> 00:06:31,050 or uppercase letters three times. 156 00:06:31,050 --> 00:06:33,560 Look at this full structure of this Go program. 157 00:06:33,560 --> 00:06:35,380 Orchestration with the weight group, 158 00:06:35,380 --> 00:06:37,540 we're gonna create two Go routines. 159 00:06:37,540 --> 00:06:39,220 Go ahead, two Go routines. 160 00:06:39,220 --> 00:06:41,210 Go ahead and create the first Go routine. 161 00:06:41,210 --> 00:06:44,330 Have it execute lowercase and through our closures 162 00:06:44,330 --> 00:06:48,134 call done to decrement the weight from two to one. 163 00:06:48,134 --> 00:06:50,884 Then you run uppercase, decrement the weight groups 164 00:06:50,884 --> 00:06:54,180 from one to zero, and you wait here until 165 00:06:54,180 --> 00:06:56,410 those Go routines report they're done 166 00:06:56,410 --> 00:06:58,180 by decrementing the weight group. 167 00:06:58,180 --> 00:06:59,750 Understand when all things are equal, 168 00:06:59,750 --> 00:07:02,120 we do not know what the schedule is going to do. 169 00:07:02,120 --> 00:07:04,680 There is no predictability on whether 170 00:07:04,680 --> 00:07:07,360 this function is gonna execute first, 171 00:07:07,360 --> 00:07:09,580 or this function is gonna execute first. 172 00:07:09,580 --> 00:07:11,300 They're both gonna be, essentially, 173 00:07:11,300 --> 00:07:14,100 created within a very narrow set 174 00:07:14,100 --> 00:07:16,163 of nanoseconds of time here. 175 00:07:17,195 --> 00:07:20,640 So by the time the main Go routine goes wait, 176 00:07:20,640 --> 00:07:23,740 okay, which is gonna force a context switch, 177 00:07:23,740 --> 00:07:26,112 we don't know what the schedule is gonna do. 178 00:07:26,112 --> 00:07:27,890 Whether it's lowercase or uppercase. 179 00:07:27,890 --> 00:07:30,520 Alright, let's run this program. 180 00:07:30,520 --> 00:07:33,763 I'm gonna go get the path of this program from the repo. 181 00:07:35,300 --> 00:07:38,020 I'm gonna come over to the terminal window, 182 00:07:38,020 --> 00:07:41,103 I'm gonna call go build, then I'm gonna run it. 183 00:07:42,610 --> 00:07:44,380 What I want you to notice 184 00:07:45,235 --> 00:07:48,490 is that the second Go routine that we created, 185 00:07:48,490 --> 00:07:51,280 this one here which runs uppercase, 186 00:07:51,280 --> 00:07:53,610 the second one we created, 187 00:07:53,610 --> 00:07:55,830 is the one that ran first. 188 00:07:55,830 --> 00:07:58,040 So you can see that this is a concurrent program. 189 00:07:58,040 --> 00:08:00,601 It didn't run in the order that we put things in, 190 00:08:00,601 --> 00:08:02,460 the schedule turned around and did that. 191 00:08:02,460 --> 00:08:03,920 Now I could run this a million times, 192 00:08:03,920 --> 00:08:06,110 and maybe it runs the same, maybe it doesn't. 193 00:08:06,110 --> 00:08:08,690 Again, when all things are equal, 194 00:08:08,690 --> 00:08:12,190 the scheduler is going to look and feel preemptive. 195 00:08:12,190 --> 00:08:14,240 It's undeterministic. 196 00:08:14,240 --> 00:08:16,418 Okay, great, so we've seen this. 197 00:08:16,418 --> 00:08:19,348 But what happens if I turn around 198 00:08:19,348 --> 00:08:22,090 and I forget to call weight? 199 00:08:22,090 --> 00:08:24,400 Look, I forget to call weight, I pull it out. 200 00:08:24,400 --> 00:08:26,360 Let's see what happens here. 201 00:08:26,360 --> 00:08:31,360 Go build, run it, there's no output. 202 00:08:32,240 --> 00:08:33,900 Start, waiting, terminating. 203 00:08:33,900 --> 00:08:35,200 What happened? 204 00:08:35,200 --> 00:08:37,920 Well this time, when I pulled the wait call out, 205 00:08:37,920 --> 00:08:40,770 the main Go routine got to finish its time slice, 206 00:08:40,770 --> 00:08:42,470 which meant main returning, 207 00:08:42,470 --> 00:08:44,470 which meant this program terminated. 208 00:08:44,470 --> 00:08:47,610 These two paths of execution that we created 209 00:08:47,610 --> 00:08:49,950 never got a chance to run. 210 00:08:49,950 --> 00:08:51,760 This is almost a race condition too, 211 00:08:51,760 --> 00:08:54,450 because we're racing to see if this program ends, 212 00:08:54,450 --> 00:08:56,800 before this scheduler makes a decision. 213 00:08:56,800 --> 00:08:58,770 Now, I kinda understand that 214 00:08:58,770 --> 00:09:00,650 orchestration and synchronization 215 00:09:00,650 --> 00:09:03,800 must be guaranteed, or it doesn't work. 216 00:09:03,800 --> 00:09:06,230 It might appear you program is working, 217 00:09:06,230 --> 00:09:08,640 but without the guarantee, real guarantees 218 00:09:08,640 --> 00:09:10,150 of organization and synchronization, 219 00:09:10,150 --> 00:09:11,450 you're just getting lucky. 220 00:09:12,620 --> 00:09:14,330 I want to show you this idea of luck 221 00:09:14,330 --> 00:09:17,260 by using a function called Gosched. 222 00:09:17,260 --> 00:09:21,390 No I don't want you using the Gosched runtime function 223 00:09:21,390 --> 00:09:23,280 in your production code. 224 00:09:23,280 --> 00:09:26,230 This, however, can be brilliant when running, 225 00:09:26,230 --> 00:09:29,030 or building, tests that have to create chaos 226 00:09:30,113 --> 00:09:31,812 because what Gosched does, 227 00:09:31,812 --> 00:09:33,220 is it makes a cooperating request. 228 00:09:33,220 --> 00:09:34,610 It is telling the scheduler, 229 00:09:34,610 --> 00:09:37,220 I am willing to give up my time, 230 00:09:37,220 --> 00:09:40,640 on the M, but the scheduler doesn't have to listen. 231 00:09:40,640 --> 00:09:42,630 The scheduler can do what ever it wants. 232 00:09:42,630 --> 00:09:45,260 This is a request, this is not a demand. 233 00:09:45,260 --> 00:09:47,010 Wait was a demand. 234 00:09:47,010 --> 00:09:51,520 Wait said I want to wait here, I demand that we wait here, 235 00:09:51,520 --> 00:09:53,480 until this semaphore count goes to zero. 236 00:09:53,480 --> 00:09:55,990 Gosched is a request. 237 00:09:55,990 --> 00:10:00,520 I request that you let some other path of execution run. 238 00:10:00,520 --> 00:10:01,793 But it's not a demand. 239 00:10:02,660 --> 00:10:04,713 Let's see what happens when I add it. 240 00:10:08,790 --> 00:10:11,350 It looks like the program worked. 241 00:10:11,350 --> 00:10:13,900 It looks like our request to 242 00:10:13,900 --> 00:10:16,400 run the other Go routines were taken. 243 00:10:16,400 --> 00:10:19,893 But understand something, there's no guarantees here. 244 00:10:19,893 --> 00:10:21,640 The scheduler, I could run this program 1000 times, 245 00:10:21,640 --> 00:10:24,220 and the scheduler can make any decision it wants, 246 00:10:24,220 --> 00:10:26,090 at any given time. 247 00:10:26,090 --> 00:10:28,818 So don't think this program worked, 248 00:10:28,818 --> 00:10:30,300 because it didn't, we got lucky. 249 00:10:30,300 --> 00:10:31,590 This is why multi-threaded 250 00:10:31,590 --> 00:10:34,280 software development is complicated. 251 00:10:34,280 --> 00:10:37,261 We need the ability to know when we need to demand 252 00:10:37,261 --> 00:10:39,250 and when we don't need to demand 253 00:10:39,250 --> 00:10:42,973 and demanding comes form synchronization and orchestration. 254 00:10:42,973 --> 00:10:44,520 Right now there is no orchestration going on here, 255 00:10:44,520 --> 00:10:46,023 this is purely a request. 256 00:10:47,005 --> 00:10:50,040 However, again, we can create a huge amount of chaos, 257 00:10:50,040 --> 00:10:55,040 in our Go tests, by causing the Go scheduler to just, 258 00:10:55,690 --> 00:10:58,490 in our kind of requesting way, 259 00:10:58,490 --> 00:11:00,330 randomly make context switches. 260 00:11:00,330 --> 00:11:02,990 Don't do this, there's no guarantees here. 261 00:11:02,990 --> 00:11:04,100 That was bad code. 262 00:11:04,100 --> 00:11:05,440 Let's go back to this. 263 00:11:05,440 --> 00:11:09,300 Now, what happens if I forget to call done? 264 00:11:09,300 --> 00:11:11,330 This happens in Go programs, right? 265 00:11:11,330 --> 00:11:13,540 I forget to call done, I forget this program, 266 00:11:13,540 --> 00:11:16,910 this Go routine forgets to report that it's done. 267 00:11:16,910 --> 00:11:19,051 I don't know, let's build it again. 268 00:11:19,051 --> 00:11:20,233 Let's run it. 269 00:11:21,470 --> 00:11:26,410 Notice this time that I have a dead lock situation. 270 00:11:26,410 --> 00:11:27,940 Yes I have a dead lock, 271 00:11:27,940 --> 00:11:30,350 and the stat trace is gonna tell us 272 00:11:30,350 --> 00:11:33,330 that our dead lock is on line, 273 00:11:33,330 --> 00:11:36,103 let me bring this out a little bit here, on line 42. 274 00:11:38,299 --> 00:11:39,440 Let's go look on line 42. 275 00:11:39,440 --> 00:11:43,150 The dead lock is on the weight, it's absolutely right. 276 00:11:43,150 --> 00:11:46,640 The weight group can no longer get to zero 277 00:11:46,640 --> 00:11:50,340 because we are no longer calling done. 278 00:11:50,340 --> 00:11:54,980 Now the Go runtime has a very simple dead lock detector. 279 00:11:54,980 --> 00:11:58,240 It's simple because all it can identify is 280 00:11:58,240 --> 00:12:02,640 when every single Go routine is now in a waiting state 281 00:12:02,640 --> 00:12:05,490 and can never move back into a runnable state. 282 00:12:05,490 --> 00:12:08,100 If you have one Go routine that can continue to run, 283 00:12:08,100 --> 00:12:11,690 we walk away from this dead lock detection. 284 00:12:11,690 --> 00:12:14,090 So we wanna try hard not to create Go routines 285 00:12:14,090 --> 00:12:18,300 in our services that kinda spin on a timer. 286 00:12:18,300 --> 00:12:20,510 You're walking away from some of this dead lock detection. 287 00:12:20,510 --> 00:12:23,080 Yes, it is simple dead lock detection, 288 00:12:23,080 --> 00:12:24,200 but it is there. 289 00:12:24,200 --> 00:12:26,270 You're seeing it, this is bad. 290 00:12:26,270 --> 00:12:28,240 The same thing can happen 291 00:12:28,240 --> 00:12:33,240 if we don't set the weight count appropriately. 292 00:12:34,730 --> 00:12:36,406 I mean look at this, 293 00:12:36,406 --> 00:12:37,870 let's say I set the weight count to one. 294 00:12:37,870 --> 00:12:40,150 That means as soon as one of these Go routines is finished, 295 00:12:40,150 --> 00:12:41,890 the weight group will go to zero. 296 00:12:41,890 --> 00:12:44,510 Again, we're gonna have some chaos, 297 00:12:44,510 --> 00:12:47,380 because we're not really managing concurrency. 298 00:12:47,380 --> 00:12:49,590 We're not waiting for both Go routines to finish. 299 00:12:49,590 --> 00:12:52,380 Look, the Go routine doing capital letters 300 00:12:52,380 --> 00:12:56,040 just finished and we don't see the next one run. 301 00:12:56,040 --> 00:13:00,230 This is all bad races, this is not proper orchestration. 302 00:13:00,230 --> 00:13:03,450 Synchronization, orchestration, is about demands, 303 00:13:03,450 --> 00:13:07,150 around guarantees, that things happen in the order we need 304 00:13:07,150 --> 00:13:10,650 or we're waiting for things to finish before we move on. 305 00:13:10,650 --> 00:13:13,370 So the weight group is a great way of doing orchestration 306 00:13:13,370 --> 00:13:15,570 when we don't need anything back from the Go routine, 307 00:13:15,570 --> 00:13:18,610 we just need to maintain the semaphore count. 308 00:13:18,610 --> 00:13:22,060 Now, let's add a little more complexity 309 00:13:22,060 --> 00:13:24,470 to this piece of code here. 310 00:13:24,470 --> 00:13:28,080 What I'm gonna do is spell example, right. 311 00:13:28,080 --> 00:13:31,150 What I'm gonna do is come in here 312 00:13:31,150 --> 00:13:33,423 and look at this next example. 313 00:13:34,420 --> 00:13:36,500 Okay, brilliant, example two. 314 00:13:36,500 --> 00:13:39,830 Now, we're gonna take the base pattern again. 315 00:13:39,830 --> 00:13:41,833 of weight group orchestration. 316 00:13:42,695 --> 00:13:44,370 Notice I'm knocking my Ps down to one, 317 00:13:44,370 --> 00:13:46,177 I've got my weight group, we Add(2), 318 00:13:47,337 --> 00:13:48,723 weight group orchestration here. 319 00:13:49,756 --> 00:13:52,110 I've got my two Go routines being called 320 00:13:52,110 --> 00:13:54,578 and run as an independent path of execution 321 00:13:54,578 --> 00:13:56,674 and then on line 42, we wait, brilliant. 322 00:13:56,674 --> 00:13:59,840 But this time, we're gonna do a little bit more work. 323 00:13:59,840 --> 00:14:01,590 This time we're calling printPrime. 324 00:14:02,812 --> 00:14:05,530 And the printPrime function identifies prime numbers 325 00:14:05,530 --> 00:14:10,530 from two to 5,000 and displays them on the screen. 326 00:14:11,050 --> 00:14:14,290 There are lots of system calls going on here 327 00:14:14,290 --> 00:14:15,820 and this work is gonna take 328 00:14:15,820 --> 00:14:17,960 a lot longer than the other work. 329 00:14:17,960 --> 00:14:19,280 What's nice about this program is 330 00:14:19,280 --> 00:14:23,166 we're going to be able to see context switches 331 00:14:23,166 --> 00:14:24,730 and see that we're not going to be able to predict 332 00:14:24,730 --> 00:14:27,033 when the context switch is going to happen. 333 00:14:28,225 --> 00:14:30,377 So, let's build this program. 334 00:14:30,377 --> 00:14:32,180 Here it is, I build it, I run it. 335 00:14:32,180 --> 00:14:34,138 There's a bunch of output. 336 00:14:34,138 --> 00:14:35,478 I'm gonna go to the top 337 00:14:35,478 --> 00:14:36,630 and you can see the second Go routine started again. 338 00:14:36,630 --> 00:14:38,863 That's, from my perspective, random. 339 00:14:39,879 --> 00:14:41,733 And what I'm doing is looking to see when 340 00:14:41,733 --> 00:14:43,639 a context switch happens. 341 00:14:43,639 --> 00:14:46,740 And it looks like B is getting a really good run here, 342 00:14:46,740 --> 00:14:48,853 and I'm gonna keep, oh, there it is. 343 00:14:49,848 --> 00:14:52,160 Okay, so we now have a context switch 344 00:14:52,160 --> 00:14:57,160 after the A Go routine got to prime number 3,833. 345 00:14:59,880 --> 00:15:02,993 Let's write the right one here, 3,833. 346 00:15:04,294 --> 00:15:08,680 Then the B Go routine now contexts to A. 347 00:15:08,680 --> 00:15:11,710 Now if I keep scrolling, what do we see, 348 00:15:11,710 --> 00:15:14,040 what do we see, what do we see. 349 00:15:14,040 --> 00:15:15,710 Does the A Go routine get to finish? 350 00:15:15,710 --> 00:15:17,630 Nope it doesn't, there it is. 351 00:15:17,630 --> 00:15:19,613 There's another context switch. 352 00:15:20,653 --> 00:15:23,130 A went to, what is that again right there, 353 00:15:23,130 --> 00:15:28,130 3041, 3041, there's a context switch. 354 00:15:29,350 --> 00:15:32,270 Then B picks up right where it left off. 355 00:15:32,270 --> 00:15:36,910 B finishes, and then A finishes. 356 00:15:36,910 --> 00:15:40,200 So then B finishes, and A finishes. 357 00:15:40,200 --> 00:15:43,400 So we did see some context switches. 358 00:15:43,400 --> 00:15:45,663 What I want to really show you is 359 00:15:45,663 --> 00:15:47,103 that these context switches are not predictable. 360 00:15:48,056 --> 00:15:51,690 I'm gonna run this again and its not gonna be 3,833. 361 00:15:51,690 --> 00:15:54,000 In fact, I have no idea what it's going to be. 362 00:15:54,000 --> 00:15:56,420 It's really not possible to predict 363 00:15:56,420 --> 00:15:58,580 the output of this program. 364 00:15:58,580 --> 00:15:59,730 So, let's run it again. 365 00:16:01,150 --> 00:16:02,630 Let's scroll to the top. 366 00:16:02,630 --> 00:16:07,210 B started again, that's fine, and here's our context switch. 367 00:16:07,210 --> 00:16:08,370 And where did it happen? 368 00:16:08,370 --> 00:16:12,380 It happened at 3,691. 369 00:16:12,380 --> 00:16:17,110 So this time it happened at 3,691. 370 00:16:17,110 --> 00:16:19,930 Again, I couldn't even predict that 371 00:16:19,930 --> 00:16:24,520 and if I just look to see if A gets to finish or not. 372 00:16:24,520 --> 00:16:28,630 Look at we're doing here, oh, there it is right there again. 373 00:16:28,630 --> 00:16:32,888 I see that we're at 4,159. 374 00:16:32,888 --> 00:16:37,888 4,159, look, A got a much larger run on this run here. 375 00:16:38,180 --> 00:16:39,640 I imagine that it's gonna finish, 376 00:16:39,640 --> 00:16:42,010 it finishes, it finishes, okay. 377 00:16:42,010 --> 00:16:43,370 My point here is that 378 00:16:43,370 --> 00:16:46,163 even though this is a cooperating scheduler, 379 00:16:47,771 --> 00:16:49,920 because the schedule is doing the cooperating and not us, 380 00:16:49,920 --> 00:16:51,720 it looks and feels preemptive. 381 00:16:51,720 --> 00:16:52,887 When all things are equal, it is undeterministic to know 382 00:16:52,887 --> 00:16:57,190 when the scheduler is going to make a scheduling decision. 383 00:16:57,190 --> 00:16:59,510 I just showed you that with this piece of code. 384 00:16:59,510 --> 00:17:01,703 I have no idea if I even run it again, 385 00:17:02,653 --> 00:17:03,770 where this is gonna happen. 386 00:17:03,770 --> 00:17:04,603 And I've run this before 387 00:17:04,603 --> 00:17:05,460 where I even see another set of 388 00:17:05,460 --> 00:17:08,200 context switches before it finishes. 389 00:17:08,200 --> 00:17:12,060 But let's now bring this completely full circle 390 00:17:12,060 --> 00:17:14,700 and go to one more example. 391 00:17:14,700 --> 00:17:18,380 This time, let's go back to the original program we had 392 00:17:18,380 --> 00:17:20,890 but just make two minor modifications. 393 00:17:20,890 --> 00:17:23,710 One, lets run this now in parallel. 394 00:17:23,710 --> 00:17:27,630 Let's use two Ps, which means two threads. 395 00:17:27,630 --> 00:17:28,930 At this point now, 396 00:17:28,930 --> 00:17:32,180 what we're gonna have is not just one P 397 00:17:32,180 --> 00:17:36,643 per thread, but two Ps, P one, 398 00:17:37,826 --> 00:17:41,400 there it is, M, P two, M, 399 00:17:41,400 --> 00:17:44,130 and then since I'm on a multi-core machine here, 400 00:17:44,130 --> 00:17:47,060 these two Ms are going to be able to run in parallel. 401 00:17:47,060 --> 00:17:49,380 In fact, our Go routines 402 00:17:49,380 --> 00:17:52,783 are gonna be able to run in parallel. 403 00:17:54,015 --> 00:17:57,437 So, maxprocs two, weight group orchestration, 404 00:17:57,437 --> 00:17:59,273 we're gonna create two Go routines, 405 00:17:59,273 --> 00:18:00,810 there it is, I'm using a full literal function 406 00:18:00,810 --> 00:18:03,880 this time to do all of the work. 407 00:18:03,880 --> 00:18:05,800 There it is, do the alphabet in lowercase three times, 408 00:18:05,800 --> 00:18:07,423 then report that we're done. 409 00:18:08,368 --> 00:18:11,910 Second Go routine, do everything in uppercase letters, 410 00:18:11,910 --> 00:18:15,023 then report that we're done and then we have the weight. 411 00:18:16,155 --> 00:18:18,714 So in this case, these two Go routines, 412 00:18:18,714 --> 00:18:21,040 they're gonna be running in parallel, making system calls, 413 00:18:21,040 --> 00:18:22,940 which means the operating system 414 00:18:24,031 --> 00:18:27,603 is going to be handling synchronization on the system calls. 415 00:18:28,671 --> 00:18:32,290 We should see a mix of output, when I run this program, 416 00:18:32,290 --> 00:18:33,870 which we didn't see before. 417 00:18:33,870 --> 00:18:38,125 If I run this enough times, we should see, 418 00:18:38,125 --> 00:18:40,459 I'm gonna look down here, you should see here 419 00:18:40,459 --> 00:18:41,770 that we now have a mix of output. 420 00:18:41,770 --> 00:18:44,480 This one's a really nice one, we see a good mix 421 00:18:44,480 --> 00:18:47,780 of output, right here on this line. 422 00:18:47,780 --> 00:18:51,050 You can see lowercase, uppercase, lowercase, uppercase. 423 00:18:51,050 --> 00:18:53,430 They're running in parallel now. 424 00:18:53,430 --> 00:18:55,565 This has almost nothing to do with 425 00:18:55,565 --> 00:18:57,177 the Go schedule, at this point. 426 00:18:57,177 --> 00:18:58,060 It's the operating system allowing 427 00:18:58,060 --> 00:19:00,220 these threads to run in parallel 428 00:19:00,220 --> 00:19:04,180 and their system calls are now being synchronized. 429 00:19:04,180 --> 00:19:06,030 So, you can see here that now 430 00:19:06,030 --> 00:19:09,890 that we've went from one P, or thread, to two, 431 00:19:09,890 --> 00:19:12,450 we're now a multi-threaded Go program. 432 00:19:12,450 --> 00:19:14,220 Go routines can run in parallel 433 00:19:14,220 --> 00:19:17,084 and this is where synchronization, orchestration 434 00:19:17,084 --> 00:19:18,870 really, really become important. 435 00:19:18,870 --> 00:19:21,240 See, Go made it super simple, 436 00:19:21,240 --> 00:19:24,560 to create these paths of executions, or Go routines, 437 00:19:24,560 --> 00:19:26,630 but you still have the burden 438 00:19:26,630 --> 00:19:29,324 and no language can take care of this, 439 00:19:29,324 --> 00:19:30,350 no runtime can take care of this. 440 00:19:30,350 --> 00:19:32,257 You still have the burden of 441 00:19:32,257 --> 00:19:33,730 synchronization and orchestration. 442 00:19:33,730 --> 00:19:36,670 I've shown you weight group orchestration. 443 00:19:36,670 --> 00:19:39,250 So, the next thing I really have to show you is 444 00:19:40,245 --> 00:19:43,173 how to deal with synchronization in the language.