1 00:00:06,910 --> 00:00:10,060 - Now that we have our signaling semantics in place, 2 00:00:10,060 --> 00:00:13,750 what I want to share with you is the three basic 3 00:00:13,750 --> 00:00:18,140 channel patterns that everything else is built on top of. 4 00:00:18,140 --> 00:00:21,160 Then we're gonna look at more advanced channel patterns. 5 00:00:21,160 --> 00:00:25,530 I cannot teach you how to be a expert multi-threaded 6 00:00:25,530 --> 00:00:28,270 software developer, this takes time and practice. 7 00:00:28,270 --> 00:00:31,020 What I can teach you are all the basic mechanics 8 00:00:31,020 --> 00:00:33,960 and semantics and continue to stress to you 9 00:00:33,960 --> 00:00:36,580 don't add this complexity until you need it. 10 00:00:36,580 --> 00:00:39,610 I wanna go through these basic channel patterns. 11 00:00:39,610 --> 00:00:41,850 We'll go through some higher level patterns, 12 00:00:41,850 --> 00:00:43,600 and then we'll start looking at more code 13 00:00:43,600 --> 00:00:46,070 as we continue through concurrency and tooling 14 00:00:46,070 --> 00:00:47,950 that leverages everything here. 15 00:00:47,950 --> 00:00:50,140 I would almost make an argument that if you're doing 16 00:00:50,140 --> 00:00:53,850 something that goes against the grain of these patterns 17 00:00:53,850 --> 00:00:56,290 I'm gonna show you doesn't necessarily mean it's wrong, 18 00:00:56,290 --> 00:00:58,800 but you're probably adding a lot of extra complexity 19 00:00:58,800 --> 00:01:01,920 to your software that maybe ya don't need. 20 00:01:01,920 --> 00:01:03,470 There are exceptions to everything, 21 00:01:03,470 --> 00:01:05,080 but maybe you don't need. 22 00:01:05,080 --> 00:01:08,470 This is the very first basic channel pattern 23 00:01:08,470 --> 00:01:13,390 around signaling, and it's called wait for task. 24 00:01:13,390 --> 00:01:16,370 Wait for task pattern is like base pattern that we're gonna 25 00:01:16,370 --> 00:01:20,660 use for higher level patterns like goroutine pooling. 26 00:01:20,660 --> 00:01:23,870 We'll leverage a wait for task type of pattern. 27 00:01:23,870 --> 00:01:25,250 Any time we look at this type of code, 28 00:01:25,250 --> 00:01:27,720 what we wanna do is, I like thinking about goroutines 29 00:01:27,720 --> 00:01:31,060 as people, it allows me to visualize the orchestrations 30 00:01:31,060 --> 00:01:33,320 that are going on and I always like the idea 31 00:01:33,320 --> 00:01:35,720 of a manager and an employee. 32 00:01:35,720 --> 00:01:37,530 There's somebody who's in charge and somebody 33 00:01:37,530 --> 00:01:40,120 who's doing work for that person's in charge. 34 00:01:40,120 --> 00:01:42,460 Cause usually if we're doing signaling, it's because 35 00:01:42,460 --> 00:01:45,400 we want someone on our behalf to do something for us, 36 00:01:45,400 --> 00:01:48,990 either while we can do other things or so we can potentially 37 00:01:48,990 --> 00:01:53,630 stop or correct that person if things are not going right. 38 00:01:53,630 --> 00:01:54,910 This is our wait for task. 39 00:01:54,910 --> 00:01:56,970 We're gonna see it again when we go to some higher level 40 00:01:56,970 --> 00:02:00,190 patterns like pooling, and this is the way it works. 41 00:02:00,190 --> 00:02:02,400 The very first line of code you see on line 34 42 00:02:02,400 --> 00:02:04,640 is the built-in function make. 43 00:02:04,640 --> 00:02:06,610 Remember, we can use make for slices, 44 00:02:06,610 --> 00:02:09,460 we can use make for maps, and we use it for channels, 45 00:02:09,460 --> 00:02:11,370 those three particular reference types. 46 00:02:11,370 --> 00:02:15,280 There's no other way to initialize a channel and put 47 00:02:15,280 --> 00:02:18,270 it into its open state other than make, 48 00:02:18,270 --> 00:02:21,670 because you can't pre-initialize data inside of a channel 49 00:02:21,670 --> 00:02:25,280 during construction, so literal construction doesn't exist. 50 00:02:25,280 --> 00:02:28,410 When we make a channel, we're gonna use the keyword chan, 51 00:02:28,410 --> 00:02:29,970 and then the type of data that we're gonna 52 00:02:29,970 --> 00:02:32,060 be using through this channel. 53 00:02:32,060 --> 00:02:34,120 Data's always typed in this language, 54 00:02:34,120 --> 00:02:35,970 so our channel's also typed. 55 00:02:35,970 --> 00:02:38,750 What you're seeing here is an unbuffered channel. 56 00:02:38,750 --> 00:02:42,760 I know it's unbuffered because I don't have a number here. 57 00:02:42,760 --> 00:02:43,593 There's no number. 58 00:02:43,593 --> 00:02:45,650 This would be a buffer channel of 10, 59 00:02:45,650 --> 00:02:46,830 but we don't have that, 60 00:02:46,830 --> 00:02:48,870 this is a unbuffered channel. 61 00:02:48,870 --> 00:02:50,770 But, what I'm really gonna look at 62 00:02:50,770 --> 00:02:52,460 on line 34 is the following. 63 00:02:52,460 --> 00:02:56,630 When I see this make call, what I see is we are creating 64 00:02:56,630 --> 00:03:00,640 a channel where we want guarantees that the signaling 65 00:03:00,640 --> 00:03:02,060 sending is being received. 66 00:03:02,060 --> 00:03:05,010 Want guarantees that the send has been received 67 00:03:05,010 --> 00:03:09,340 and, right now, we're gonna also signal with data 68 00:03:09,340 --> 00:03:11,110 where the data is string. 69 00:03:11,110 --> 00:03:13,170 You see, I'm putting everything within the scope 70 00:03:13,170 --> 00:03:15,690 of the semantics that we talked about. 71 00:03:15,690 --> 00:03:19,910 Moving away from the syntax and moving into the semantics. 72 00:03:19,910 --> 00:03:22,940 This code is saying we want guarantees 73 00:03:22,940 --> 00:03:26,040 and we're gonna signal with string data. 74 00:03:26,040 --> 00:03:27,210 Perfect. 75 00:03:27,210 --> 00:03:29,920 The channel is nothing more than a pointer variable 76 00:03:29,920 --> 00:03:32,470 to a very complex data structure underneath. 77 00:03:32,470 --> 00:03:35,523 Again, we're gonna be using value semantics all the time. 78 00:03:36,440 --> 00:03:37,420 Wait for task. 79 00:03:37,420 --> 00:03:38,253 Here's the idea. 80 00:03:38,253 --> 00:03:42,140 I'm gonna go recruit some human being, some employee, 81 00:03:42,140 --> 00:03:45,100 to go ahead and so some work for me, 82 00:03:45,100 --> 00:03:48,540 and that's representative of the literal function 83 00:03:48,540 --> 00:03:50,980 and the function call-in in the keyword go. 84 00:03:50,980 --> 00:03:52,160 Here it is again. 85 00:03:52,160 --> 00:03:56,910 I've gone off and I've launched a goroutine to do some work. 86 00:03:56,910 --> 00:03:58,993 We could imagine the following scenario. 87 00:04:00,734 --> 00:04:03,670 I want you to always consider yourself to be the manager. 88 00:04:03,670 --> 00:04:05,683 This is our path of execution. 89 00:04:06,570 --> 00:04:10,060 Here it is, here's the manager, and now we go off and we say 90 00:04:10,060 --> 00:04:13,390 we want an employee to go help us do something. 91 00:04:13,390 --> 00:04:16,650 Here we are, and we go launch a second 92 00:04:16,650 --> 00:04:20,230 path of execution right here, on line 36. 93 00:04:20,230 --> 00:04:22,730 This is our goroutine right now that's executing 94 00:04:22,730 --> 00:04:24,800 the function, that's us, and now this is 95 00:04:24,800 --> 00:04:27,620 the employee goroutine on line 36. 96 00:04:27,620 --> 00:04:29,610 I want you to look on line 37. 97 00:04:29,610 --> 00:04:32,110 On line 37, what you see is a channel receive, 98 00:04:32,110 --> 00:04:34,268 it's a unary operation. 99 00:04:34,268 --> 00:04:39,268 The operator arrow is attached to the channel variable. 100 00:04:40,073 --> 00:04:43,070 Unary operation, and this is a channel receive, 101 00:04:43,070 --> 00:04:46,870 so what we have right now is this receive 102 00:04:46,870 --> 00:04:51,510 on our channel, and this is a blocking call, 103 00:04:51,510 --> 00:04:54,240 because we want guarantees. 104 00:04:54,240 --> 00:04:56,610 Basically, we're blocked right here. 105 00:04:56,610 --> 00:05:01,020 We're basically saying hey, employee, come here. 106 00:05:01,020 --> 00:05:02,190 Let's get involved. 107 00:05:02,190 --> 00:05:04,760 Here you are, you now exist, you're moving. 108 00:05:04,760 --> 00:05:06,230 But I need you to wait. 109 00:05:06,230 --> 00:05:10,260 We're basically saying here is wait 110 00:05:10,260 --> 00:05:13,500 for us to tell you what to do. 111 00:05:13,500 --> 00:05:15,620 How long is the employee gonna wait? 112 00:05:15,620 --> 00:05:16,910 It is unknown. 113 00:05:16,910 --> 00:05:21,400 Remember, we have unknown latency here, 114 00:05:21,400 --> 00:05:24,460 because we are going after guarantees. 115 00:05:24,460 --> 00:05:27,760 We wanna guarantee that this employee here 116 00:05:27,760 --> 00:05:30,260 is going to have received our signal. 117 00:05:30,260 --> 00:05:31,780 You wait, you don't know what to do yet. 118 00:05:31,780 --> 00:05:34,200 I'm gonna send it to you, you wait. 119 00:05:34,200 --> 00:05:38,570 That means that at this point, this goroutine is blocked, 120 00:05:38,570 --> 00:05:39,403 waiting for us. 121 00:05:40,279 --> 00:05:43,810 In the meantime, we get to continue to run in parallel 122 00:05:43,810 --> 00:05:46,760 and what we do is we get to line 41. 123 00:05:46,760 --> 00:05:50,750 Line 41 is simulating some work that we have to do. 124 00:05:50,750 --> 00:05:52,340 This is on line 41. 125 00:05:52,340 --> 00:05:54,190 Some work that we have to do to prepare 126 00:05:54,190 --> 00:05:56,260 the work for our employee. 127 00:05:56,260 --> 00:05:58,150 How long are we gonna wait on line 41? 128 00:05:58,150 --> 00:05:58,983 We don't know. 129 00:05:58,983 --> 00:06:01,760 I've even tried to simulate the unknown latency here 130 00:06:01,760 --> 00:06:04,770 with the time.Sleep function, and a random 131 00:06:04,770 --> 00:06:07,088 number generator for duration. 132 00:06:07,088 --> 00:06:08,583 We have no idea. 133 00:06:08,583 --> 00:06:11,771 They have no idea how long they're gonna wait for us, 134 00:06:11,771 --> 00:06:15,100 cause we have no idea how long this work is going to take. 135 00:06:15,100 --> 00:06:17,370 Eventually, we get past line 41, 136 00:06:17,370 --> 00:06:19,460 and now we get to our signaling. 137 00:06:19,460 --> 00:06:23,270 Our send side of the signaling, which is right here. 138 00:06:23,270 --> 00:06:26,010 Notice sending is a binary operation. 139 00:06:26,010 --> 00:06:28,220 Receiving is a unary operation. 140 00:06:28,220 --> 00:06:29,980 Then it's binary, cause we're gonna signal 141 00:06:29,980 --> 00:06:33,420 with data, with string data. 142 00:06:33,420 --> 00:06:37,240 Our work is now prepared and now what we're going to do 143 00:06:38,185 --> 00:06:42,040 is we're gonna take this work that we have here 144 00:06:42,040 --> 00:06:44,600 and we're gonna be sending it. 145 00:06:44,600 --> 00:06:48,540 We're gonna send it to this other employee. 146 00:06:48,540 --> 00:06:50,640 They're waiting right here on the receive. 147 00:06:50,640 --> 00:06:52,070 We're gonna do the send. 148 00:06:52,070 --> 00:06:54,690 Notice, the syntax looks like this. 149 00:06:54,690 --> 00:06:58,780 It's the channel and it's the same arrow operator 150 00:06:58,780 --> 00:07:02,250 where we're showing that he data is getting placed 151 00:07:02,250 --> 00:07:04,440 into the channel. 152 00:07:04,440 --> 00:07:06,740 We're gonna do the sending here. 153 00:07:06,740 --> 00:07:09,020 Remember, we wanted guarantees. 154 00:07:09,020 --> 00:07:12,400 This is blocking, waiting for corresponding receive. 155 00:07:12,400 --> 00:07:14,010 This receive is in place. 156 00:07:14,010 --> 00:07:17,740 Now what we have here is the send. 157 00:07:17,740 --> 00:07:22,670 With the send in place now, we come here and what we now 158 00:07:22,670 --> 00:07:27,670 have is the send and the receive together, finally. 159 00:07:28,220 --> 00:07:31,410 Again, to get the guarantee, the receive happens 160 00:07:31,410 --> 00:07:33,830 nanoseconds before the send. 161 00:07:33,830 --> 00:07:36,670 But we're both kind of in a blocking situation here. 162 00:07:36,670 --> 00:07:37,780 We are. 163 00:07:37,780 --> 00:07:41,160 This goroutine is now in a block state, in a wait state. 164 00:07:41,160 --> 00:07:43,620 This has been in a wait state and the only way 165 00:07:43,620 --> 00:07:47,740 to keep moving now is to let this channel operation finish. 166 00:07:47,740 --> 00:07:50,550 What we're gonna see here is that the receive 167 00:07:50,550 --> 00:07:54,940 is gonna happen nanoseconds before, 168 00:07:54,940 --> 00:07:57,400 and the receive is gonna start first. 169 00:07:57,400 --> 00:07:59,030 This is gonna happen first 170 00:07:59,030 --> 00:08:00,650 and it's gonna be able to move on. 171 00:08:00,650 --> 00:08:05,090 Once the receive is done, then we're gonna move on second. 172 00:08:05,090 --> 00:08:07,860 This is gonna happen first, that's gonna happen second. 173 00:08:07,860 --> 00:08:09,223 Nanoseconds. 174 00:08:10,180 --> 00:08:14,410 We're gonna get, when we get to line 43, is we're gonna get 175 00:08:14,410 --> 00:08:18,590 a guarantee that the receive has happened. 176 00:08:18,590 --> 00:08:21,820 Once this goroutine gets the data that we're sending 177 00:08:21,820 --> 00:08:24,230 over this channel, they get to do their thing. 178 00:08:24,230 --> 00:08:26,220 Right now, we're just displaying information 179 00:08:26,220 --> 00:08:29,200 and they finish, and then we just move on, 180 00:08:29,200 --> 00:08:31,300 and we're running in parallel. 181 00:08:31,300 --> 00:08:34,910 This stuff gets confusing because the only thing 182 00:08:34,910 --> 00:08:37,920 that's atomic is this operation here. 183 00:08:41,056 --> 00:08:44,170 That is the only thing that's atomic. 184 00:08:44,170 --> 00:08:45,313 Understand that. 185 00:08:46,450 --> 00:08:47,283 Excuse me. 186 00:08:47,283 --> 00:08:49,070 I've got two goroutines. 187 00:08:49,070 --> 00:08:51,900 Goroutine One, Goroutine Two, 188 00:08:51,900 --> 00:08:55,000 each running on their own core in parallel. 189 00:08:55,000 --> 00:09:00,000 They both are put into this wait state when we get here, 190 00:09:00,180 --> 00:09:02,270 because they've gotta have the guarantee. 191 00:09:02,270 --> 00:09:03,460 That's what we asked for. 192 00:09:03,460 --> 00:09:06,050 After that, these two goroutines are running 193 00:09:06,050 --> 00:09:08,470 independent of each other on their own thread 194 00:09:08,470 --> 00:09:10,400 from an operating system perspective. 195 00:09:10,400 --> 00:09:11,440 What does that mean? 196 00:09:11,440 --> 00:09:13,500 It means that these print statements 197 00:09:13,500 --> 00:09:18,500 can technically happen in any order. 198 00:09:18,860 --> 00:09:23,270 There's no guarantee that the print statement on Line 38 199 00:09:23,270 --> 00:09:26,410 is gonna happen before the print statement on line 43, 200 00:09:26,410 --> 00:09:28,510 because the only thing that's blocked here, 201 00:09:28,510 --> 00:09:32,110 within nanoseconds of a time, is the channel operation. 202 00:09:32,110 --> 00:09:35,460 This is why it makes it very hard to use print statements 203 00:09:35,460 --> 00:09:38,880 to determine order, because the only thing guaranteed 204 00:09:38,880 --> 00:09:42,620 is the receive is happening before the send. 205 00:09:42,620 --> 00:09:47,170 If I try to build this program just running wait for task, 206 00:09:47,170 --> 00:09:51,320 which I've set up here, you can see on this run, 207 00:09:51,320 --> 00:09:55,320 it's showing you that the send happened before the receive. 208 00:09:55,320 --> 00:09:57,760 If we don't realize what I just told you, 209 00:09:57,760 --> 00:10:00,480 that the receive happens before the send, nanosecond, 210 00:10:00,480 --> 00:10:02,810 we would think by the output that the send 211 00:10:02,810 --> 00:10:06,450 happened before the receive, but that's not what happened. 212 00:10:06,450 --> 00:10:07,810 Because this is only happening 213 00:10:07,810 --> 00:10:10,490 within nanoseconds of time apart. 214 00:10:10,490 --> 00:10:14,850 Both goroutines really kind of start just running 215 00:10:14,850 --> 00:10:16,650 again at the same time. 216 00:10:16,650 --> 00:10:20,400 If I ran this a few more times, we might get lucky 217 00:10:20,400 --> 00:10:23,850 and see the receive output happen before the send. 218 00:10:23,850 --> 00:10:26,320 There's no guarantees that this will happen, 219 00:10:26,320 --> 00:10:29,140 but the fact that we're seeing, ah there it is right there. 220 00:10:29,140 --> 00:10:30,810 Beautiful, look right there. 221 00:10:30,810 --> 00:10:33,640 Suddenly, now, the receive looks like 222 00:10:33,640 --> 00:10:35,410 it happened before the send (laughing). 223 00:10:35,410 --> 00:10:39,330 This is why so many people get confused with concurrency, 224 00:10:39,330 --> 00:10:43,130 because they're looking at the order of things and the only 225 00:10:43,130 --> 00:10:46,610 order is atomically at that actual send and receive. 226 00:10:46,610 --> 00:10:49,020 After that, all bets are off. 227 00:10:49,020 --> 00:10:52,560 You cannot be using print statements to look at ordering. 228 00:10:52,560 --> 00:10:53,950 I've left the print statements 229 00:10:53,950 --> 00:10:55,950 in here to prove that to you. 230 00:10:55,950 --> 00:10:57,890 When you're working with and learning concurrency, 231 00:10:57,890 --> 00:10:59,930 remember your print statements are not gonna help you. 232 00:10:59,930 --> 00:11:02,330 They're gonna really confuse you. 233 00:11:02,330 --> 00:11:05,660 This is our wait for task. 234 00:11:05,660 --> 00:11:10,660 Again, we're gonna use this type of pattern for pooling.