1 00:00:00,800 --> 00:00:04,800 Since almost all go code uses go routines and 2 00:00:05,100 --> 00:00:09,900 unbuffered channels. You may not really thought very much about why they're 3 00:00:09,900 --> 00:00:13,900 on buffered or what it means when you're actually transmitting or receiving 4 00:00:13,900 --> 00:00:17,800 on a channel. That is unbuffered, but I think it's worth thinking 5 00:00:17,800 --> 00:00:21,900 about it a little bit because Channel communication, when I'm buffered is 6 00:00:21,900 --> 00:00:25,400 really both, passing a piece of information and explicit 7 00:00:25,400 --> 00:00:29,900 synchronization just to illustrate that. Let's make a little program and look, 8 00:00:30,000 --> 00:00:34,800 At how that synchronization works. So I'm going to make a program 9 00:00:34,800 --> 00:00:36,500 that runs a go routine. 10 00:00:38,300 --> 00:00:41,800 And that girl routine is going to loop around forever. 11 00:00:43,100 --> 00:00:46,800 And provide numbers. So it's a very simple thing is going to be 12 00:00:47,400 --> 00:00:50,400 ready to transmit. Let's make ourselves a channel. 13 00:00:53,700 --> 00:00:57,800 Channel of integers. And here we've got to say down that channel. 14 00:00:57,800 --> 00:01:00,600 I will send I and then I will add 1/2. I 15 00:01:02,600 --> 00:01:06,900 So, this will Loop forever and it will output a an integer on 16 00:01:06,900 --> 00:01:10,800 that channel and loop around ready to the river. Now, if 17 00:01:10,800 --> 00:01:14,800 nobody is willing to receive on that channel, this does absolutely 18 00:01:14,800 --> 00:01:18,200 nothing at all. It's sits right here on this line waiting. 19 00:01:18,800 --> 00:01:22,700 So that's where a synchronization is happening. Has to have someone else ready to 20 00:01:22,700 --> 00:01:25,700 read in order for that to work. So we can make another 21 00:01:26,400 --> 00:01:30,300 simple thing here. We're going to say, okay, I'm going to read 22 00:01:34,800 --> 00:01:35,900 A value here. 23 00:01:39,600 --> 00:01:42,500 And then we go and we should be able to run that. 24 00:01:44,600 --> 00:01:48,500 and we got zero because that was the first value and if we read it a few more times, 25 00:01:49,900 --> 00:01:53,800 We'd get the next few values. So what's nice about 26 00:01:53,800 --> 00:01:57,000 this is it's dead. Easy to think about this thing 27 00:01:57,800 --> 00:02:01,700 is going nowhere until someone's ready to read. And this thing is going 28 00:02:01,700 --> 00:02:05,400 nowhere until somebody is actually ready to write. And in general, 29 00:02:05,400 --> 00:02:09,900 unbuffered channels make it very, very easy to reason about how your program is working because they will 30 00:02:09,900 --> 00:02:13,100 stop operating go routines on the main part of the function 31 00:02:13,500 --> 00:02:17,800 until it actually needs to transmit or read on that particular Channel and until two 32 00:02:17,800 --> 00:02:19,400 people are willing to cooperate. 33 00:02:19,700 --> 00:02:23,900 Evening, quite complicated systems where there many things are Reading Writing on channels. It's 34 00:02:23,900 --> 00:02:27,800 very easy to understand what's happening. Now, an obvious thing you have to 35 00:02:27,800 --> 00:02:31,800 do in programs is tell the program to stop. 36 00:02:31,900 --> 00:02:35,900 So there's a couple of ways to do this one would be just to close the channel and 37 00:02:35,900 --> 00:02:39,800 then it will stop being able to transmit or receive on it, but you can 38 00:02:39,800 --> 00:02:43,700 get a Terrors that way because trying to write on a closed channel, will 39 00:02:43,700 --> 00:02:47,900 generate an error. A typical way to do. This is actually to have another Channel used 40 00:02:47,900 --> 00:02:49,600 for telling the program just to give 41 00:02:49,700 --> 00:02:53,800 Give up. So let's the modifiers little bit or make ourselves 42 00:02:53,800 --> 00:02:57,500 a channel. The channel doesn't actually have anything on it. 43 00:02:57,800 --> 00:03:01,800 Just an empty struct song. And within here, we're going to do two 44 00:03:01,800 --> 00:03:02,300 things. 45 00:03:04,300 --> 00:03:08,800 I'm going to go in here, I'm going to say I'm ready to send that and what I've done and I'll update it. 46 00:03:09,500 --> 00:03:13,900 I'm also ready to receive on the done Channel, and when that happens, this program 47 00:03:13,900 --> 00:03:17,700 will terminate. So again, it's looping around 48 00:03:18,000 --> 00:03:22,900 is adding one every time it's given out a number so you'll never give out the same number twice, but if it gets 49 00:03:22,900 --> 00:03:26,900 told to terminate, it will terminate. And so we can do what we've 50 00:03:26,900 --> 00:03:30,900 done here and read. And then we can do an explicit, send a 51 00:03:30,900 --> 00:03:31,600 message. 52 00:03:34,000 --> 00:03:38,400 And this case was going to send the empty empty struct. That's the definition, that's the actual 53 00:03:38,900 --> 00:03:42,700 value, which is empty. And then let's see what happens if we try and 54 00:03:42,700 --> 00:03:46,900 read on that channel. So that channel is still alive. But 55 00:03:47,300 --> 00:03:51,900 this, this thing will have terminated is only choice once it's that has happened. And by 56 00:03:51,900 --> 00:03:55,900 the way, if this happens that happens, that's 57 00:03:55,900 --> 00:03:59,800 what the synchronization really means, you know, that, those two things have happened, so this 58 00:03:59,800 --> 00:04:03,500 will absolutely have happened, so there's no chance for this to read. So let's just run. 59 00:04:04,500 --> 00:04:08,500 And there you go. All go routines, are asleep. Deadlock, this could never receive. It 60 00:04:08,500 --> 00:04:12,500 knows that there's actually nothing that could could receive on this. 61 00:04:12,800 --> 00:04:16,700 So there has actually genuinely terminated now changing that to a 62 00:04:16,700 --> 00:04:20,700 close on that channel will cause this case statement to terminate the 63 00:04:20,700 --> 00:04:24,700 next time that we'd actually goes around, but you can 64 00:04:24,700 --> 00:04:28,800 see from running this program that we've introduced a little bit of non-determinism 65 00:04:28,800 --> 00:04:29,600 by run it. 66 00:04:31,700 --> 00:04:35,800 That way, it worked, but occasionally, you'll get all go, routines are asleep, depending on which 67 00:04:35,800 --> 00:04:39,800 happened, we've actually broken, that rule about the synchronization, they're the nice 68 00:04:39,800 --> 00:04:43,900 thing about transmitting on that channel is your guarantee that somebody this guy had to be receiving 69 00:04:43,900 --> 00:04:47,800 and therefore, you know, that the channel did actually terminated at 70 00:04:47,800 --> 00:04:51,400 that point. So in general just sending something down a channel, you get that 71 00:04:51,400 --> 00:04:54,700 synchronization point and you're absolutely sure what has happened. 72 00:04:55,900 --> 00:04:59,400 All of the unbuffered channels I used here are 73 00:04:59,400 --> 00:05:03,500 both read and write anyone can read and write to them. It's 74 00:05:03,500 --> 00:05:07,700 sometimes useful to restrict that to read only channels and 75 00:05:07,700 --> 00:05:11,900 right only channels now, they may seem like kind of a strange thing to have 76 00:05:11,900 --> 00:05:15,700 and in fact you can take a read write Channel and pass it to 77 00:05:15,700 --> 00:05:19,700 another function saying this is only readable or only writable 78 00:05:20,000 --> 00:05:24,900 so I'm going to do a little bit of refactoring here and make this thing a bit more interesting and then show how to do 79 00:05:24,900 --> 00:05:25,000 that. 80 00:05:25,100 --> 00:05:29,800 At to prevent any sorts of concurrency mistakes. So, instead of having 81 00:05:29,800 --> 00:05:33,800 all the stuff to find in Maine here, I'm going to pull this out and I'm going to 82 00:05:33,800 --> 00:05:36,800 make myself a new 83 00:05:36,800 --> 00:05:40,700 type, which is going to be this counter, 84 00:05:40,700 --> 00:05:43,400 and it's going to have the channel in it. 85 00:05:44,900 --> 00:05:47,000 And it's also going to have the done Channel. 86 00:05:49,300 --> 00:05:53,600 And it's going to have integer and we'll make ourselves a little function to create one of 87 00:05:53,600 --> 00:05:54,100 these. 88 00:05:58,100 --> 00:06:01,600 so create one of these and just going to go 89 00:06:04,600 --> 00:06:08,900 I can you counter and then we'll just make the channels within 90 00:06:08,900 --> 00:06:12,700 it and he's going to be bi-directional. There's nothing in here that says there 91 00:06:12,700 --> 00:06:16,700 anything other than normal buffered channels above 92 00:06:16,700 --> 00:06:20,000 channels and I is set to 0 so we don't need to 93 00:06:22,200 --> 00:06:25,100 That's that crazy new counter. So get one of these guys here. 94 00:06:28,500 --> 00:06:32,800 Okay, so we've got a new counter here and going to want to be able to get 95 00:06:32,800 --> 00:06:36,900 at a channel to read from, but I'm not going to return C. So I'm going 96 00:06:36,900 --> 00:06:38,300 to have a method on this. 97 00:06:39,800 --> 00:06:42,400 And we're going to say on the counter. 98 00:06:45,500 --> 00:06:49,300 Go to the source. It's called I'm going to say we're going to channel 99 00:06:49,300 --> 00:06:53,200 which you can only read from. So this is the Syntax for that. 100 00:06:53,600 --> 00:06:57,900 So this little arrow here, so we can reach me and I'm just going to return 101 00:06:57,900 --> 00:07:01,900 C dot C. I haven't got this. 102 00:07:01,900 --> 00:07:03,800 I'm going to say 103 00:07:05,700 --> 00:07:07,700 Seedot, get sauce. 104 00:07:09,100 --> 00:07:11,200 And just change all this to read. 105 00:07:14,100 --> 00:07:14,600 and, 106 00:07:16,600 --> 00:07:19,100 I'm going to have another function here to stop it, 107 00:07:22,800 --> 00:07:26,300 which is the thing that's going to send a message down is done Channel. 108 00:07:30,600 --> 00:07:34,800 Okay. And the other thing we're going to watch to is when this guy starts is 109 00:07:35,500 --> 00:07:38,800 start the magic go routine that does all the work. 110 00:07:40,800 --> 00:07:44,900 It's like this. And now we have access to those channels are going to say 111 00:07:48,900 --> 00:07:51,800 where we going to do that. And at one time, 112 00:07:53,100 --> 00:07:55,800 and we're always running to 113 00:08:00,500 --> 00:08:02,000 Receive on the done Channel. 114 00:08:03,200 --> 00:08:04,600 And give up. 115 00:08:06,800 --> 00:08:10,400 And then we go. So when you create a new account, you get this girl routine 116 00:08:10,400 --> 00:08:14,900 running and you can terminate it by sending a message to their, which, in fact, we do using that. 117 00:08:16,300 --> 00:08:20,500 And I think we're in good shape, let's give it a run through what I 118 00:08:20,500 --> 00:08:21,500 forgot. Oops. 119 00:08:27,900 --> 00:08:30,800 Yes, it's counter 120 00:08:31,900 --> 00:08:34,600 and let's counter make sure that's correct 121 00:08:35,400 --> 00:08:36,500 and good. 122 00:08:38,200 --> 00:08:40,600 Very good and ways my done Channel. 123 00:08:43,500 --> 00:08:47,200 Get rid of that and it still it to stop. 124 00:08:50,200 --> 00:08:54,500 So same, same kind of situation here with the thing running and we tell it to 125 00:08:54,500 --> 00:08:58,500 stop and obviously got the error because I tried to read 3.2 there. 126 00:08:59,300 --> 00:09:03,600 But what you'll notice is that this channel here read, which is 127 00:09:03,600 --> 00:09:07,700 actually the channel, That was originally defined, see which is defined here, 128 00:09:07,900 --> 00:09:11,800 has been magically turned into a read-only channel and if I try to mess with it, 129 00:09:12,000 --> 00:09:16,300 let's just suppose that in here, I decided to try to write to it. 130 00:09:18,900 --> 00:09:22,900 You'll see that I get a a compile time error and you cannot write to us it's 131 00:09:22,900 --> 00:09:26,900 going to be useful for controlling who has access to channels and can 132 00:09:27,000 --> 00:09:31,600 in some circumstances make it easier when you're writing code to reason about it because you know, 133 00:09:31,600 --> 00:09:35,500 okay only this person has access to go to write to it or read from it 134 00:09:36,000 --> 00:09:40,800 in the counter example. I used a channel to signal that the counter go routine 135 00:09:40,800 --> 00:09:44,900 should terminate. That was the done Channel and that works great. If you have a single go 136 00:09:44,900 --> 00:09:48,600 routine but it's quite common that you might have many. 137 00:09:48,700 --> 00:09:52,800 Routines and there might be a need to do some sort of termination of lots of them. 138 00:09:53,100 --> 00:09:57,800 Now, you can share the done Channel, across, lots of goroutines, and then you 139 00:09:57,800 --> 00:10:01,700 can close it or you can try and send a message to it. If you send a 140 00:10:01,700 --> 00:10:05,900 message to her, only one of them's going to pick up because of synchronization. If you close that 141 00:10:05,900 --> 00:10:09,300 channel, it acts a bit like a broadcast and everyone who was trying to read on it 142 00:10:09,700 --> 00:10:13,600 will then stop reading what unable to read and their go routines will 143 00:10:13,600 --> 00:10:14,300 terminate 144 00:10:15,200 --> 00:10:19,900 Another way to do this is to wrap all that logic up into another type 145 00:10:19,900 --> 00:10:23,900 and there's a rather. Nice example from the folks at Google were they invented, what they 146 00:10:23,900 --> 00:10:27,900 called a context and that wraps, this kind of functionality and users read 147 00:10:27,900 --> 00:10:31,700 only channels. So one of things I've done here is I've changed the counter 148 00:10:31,900 --> 00:10:35,800 to use a context and context is really quite a simple thing 149 00:10:35,800 --> 00:10:39,900 is just in this instance it just has a done Channel within it 150 00:10:40,200 --> 00:10:44,800 and when you create a new one of them, you get a new contacts with a ready to go. 151 00:10:45,000 --> 00:10:49,500 Channel and when you call stop on that context it closes that channel. 152 00:10:49,700 --> 00:10:53,500 So calling stop on the context will tell you one who's using that context. They have to 153 00:10:53,500 --> 00:10:57,600 terminate and anyone who's using that will use this get 154 00:10:57,600 --> 00:11:01,900 done method to get the done Channel and rather than getting 155 00:11:01,900 --> 00:11:05,000 access to it explicitly, they get access to a 156 00:11:05,400 --> 00:11:09,800 read-only version of it. This prevents any other routine from deciding to close 157 00:11:09,800 --> 00:11:13,500 that channel, which could cause problems. For example, you can't close the channel twice 158 00:11:13,800 --> 00:11:14,700 so you tab it have. 159 00:11:14,900 --> 00:11:18,800 An issue there by having a single place where it gets closed. Then you 160 00:11:18,800 --> 00:11:22,800 could ensure that it's only closed once and by only handing out read 161 00:11:22,800 --> 00:11:26,900 only channels, no one else can close it. And if someone tried then at compile time you get 162 00:11:26,900 --> 00:11:30,800 an error. And then what I did was, I changed the counter structure 163 00:11:31,500 --> 00:11:35,900 so that includes a context and that gets passed in when you create 164 00:11:35,900 --> 00:11:39,400 the new counter and the cost is stored. And then 165 00:11:40,000 --> 00:11:44,700 we just grab the done channel from the context and read from it as before. 166 00:11:45,100 --> 00:11:49,800 Now, to make sure everything is clean and terminates nicely. I actually used a white group 167 00:11:49,800 --> 00:11:53,900 from the sink package. This is one of the things in the sink package which is really nice. It allows 168 00:11:53,900 --> 00:11:57,900 you to wait until some number of go routines. Have terminated 169 00:11:57,900 --> 00:12:01,800 the way you do that is you declare a weight group and then every time you add a 170 00:12:01,800 --> 00:12:05,700 go routine you call AD which adds to a counter and every 171 00:12:05,700 --> 00:12:09,900 time a go routine terminates you call done which will decrement the counter here. I done 172 00:12:09,900 --> 00:12:13,900 it using a defer for Simplicity and when you want to make sure everything is 173 00:12:13,900 --> 00:12:14,800 done. You see it? 174 00:12:14,900 --> 00:12:18,900 And do wait. So what this will do is will do exactly what happened before. It's going 175 00:12:18,900 --> 00:12:22,700 to read three numbers from the read only channel from the counter. 176 00:12:23,100 --> 00:12:27,800 Is then going to call the context and tell the context. Please, stop whatever is running out there 177 00:12:28,200 --> 00:12:32,900 and then we'll just wait until things terminate and just to make it really clear. I added a printf in here 178 00:12:33,000 --> 00:12:36,800 saying you know what the counter is now terminated. So if we run it 179 00:12:38,200 --> 00:12:42,900 What we're going to see is we read those three numbers? We call contact, stop it closes 180 00:12:42,900 --> 00:12:46,500 this channel here, done and the counter terminates. Everything is 181 00:12:46,900 --> 00:12:50,800 cleaned up really nicely with this idea of a context. If you read 182 00:12:50,800 --> 00:12:54,200 about Google's implementation, there are many other interesting details of what they pass around in a 183 00:12:54,200 --> 00:12:58,900 context, but this basic idea is extremely powerful. So to illustrate, this concept 184 00:12:58,900 --> 00:13:02,800 of a context where it closes a channel to Signal termination to a number of 185 00:13:02,800 --> 00:13:06,700 different go routines. I've added something else to this program which 186 00:13:06,700 --> 00:13:07,800 is another 187 00:13:08,300 --> 00:13:12,800 Type which doubles something so it what it does is it goes 188 00:13:12,800 --> 00:13:16,800 through and it reads from a an in 189 00:13:16,800 --> 00:13:20,800 channel here. When you make one of these new doublers and it outputs 190 00:13:20,900 --> 00:13:24,400 on an out Channel. This thing here, whatever that number is 191 00:13:24,500 --> 00:13:28,500 doubled as you can see this slowly unfamiliar looking syntax here which is 192 00:13:28,500 --> 00:13:32,600 saying, read on in double it and write it on out 193 00:13:32,900 --> 00:13:36,300 and if we get a done termination then we will 194 00:13:36,300 --> 00:13:37,200 terminate 195 00:13:37,900 --> 00:13:41,400 that done Channel came from a context to 196 00:13:41,400 --> 00:13:45,700 context was passed into the doubler. As what happens in the in Maine 197 00:13:46,300 --> 00:13:50,900 is that I create a single context, use it to create a counter, get 198 00:13:50,900 --> 00:13:54,600 that counters output Channel. I'm always giving 012 199 00:13:55,500 --> 00:13:59,900 create a doubler, same contact pass it that channel. So it can now read whatever 200 00:14:00,100 --> 00:14:04,900 the counter is creating get the double as output, which is now going to be 201 00:14:04,900 --> 00:14:07,800 0 to 4 etcetera. And then read from it. 202 00:14:08,700 --> 00:14:12,900 Close the context. I'm done close the channel Singletary. Body has stopped and 203 00:14:12,900 --> 00:14:16,900 I use the weight group just so I can see clean termination from the program. And again 204 00:14:16,900 --> 00:14:20,900 I put in a message here saying doubler terminated and I also have 205 00:14:21,000 --> 00:14:25,100 counter terminated. So let's run this program and see what happens. 206 00:14:28,400 --> 00:14:32,900 It seemed like it worked. Fine, we got some doubles. Obviously, those two things have been linked together, 207 00:14:33,000 --> 00:14:37,800 we call the context stop and the program terminated. So the termination part is 208 00:14:37,800 --> 00:14:41,800 working very, very nicely. It cleans up very well and you can imagine having many different 209 00:14:41,800 --> 00:14:45,800 go routines waiting on that done Channel and you get a 210 00:14:45,800 --> 00:14:49,900 context stop called and everything cleans up nicely and obviously the 211 00:14:49,900 --> 00:14:53,900 program is terminated. So the weight group is actually finished which means we'll go routines, 212 00:14:53,900 --> 00:14:57,900 are finished, so that's fantastic. However actually there's a really nasty Pitfall in this program if I 213 00:14:57,900 --> 00:14:58,100 run it. 214 00:14:58,300 --> 00:15:02,800 Again if I'm lucky I'm going to get an error, right? So as you can see, something 215 00:15:02,800 --> 00:15:06,700 bad has happened. You see that the counter is terminated, but you can see 216 00:15:06,700 --> 00:15:10,700 that the double it doesn't seem to. There's no message saying it and there's also an error 217 00:15:10,700 --> 00:15:14,800 about everything having gone to sleep. The reason that happens is go back to that. Rather 218 00:15:14,800 --> 00:15:18,900 ugly looking line here. If we think about this carefully, this can actually 219 00:15:18,900 --> 00:15:22,900 create this deadlock situation. What can happen is we can read a 220 00:15:22,900 --> 00:15:24,500 value in on D in 221 00:15:25,300 --> 00:15:29,800 And then nobody's perhaps what willing to listen, Tandy 222 00:15:29,800 --> 00:15:33,700 out to read something because in fact, we've terminated as it we're stuck 223 00:15:34,200 --> 00:15:38,700 and so we have to change this little bit whenever you see this kind of thing you'd better watch out because this is it's 224 00:15:38,700 --> 00:15:42,900 not simple to reason about because there's different synchronization points. So we're going to break 225 00:15:42,900 --> 00:15:45,500 it up a little bit, so we're going to start by saying 226 00:15:47,600 --> 00:15:48,800 I'm going to read a value. 227 00:15:50,400 --> 00:15:54,900 Sighs, pretty clear. So either reader value or terminate and in the case I read a value, 228 00:15:55,000 --> 00:15:59,400 then I'm willing to do one or two other things. I'm willing to provide that thing 229 00:15:59,900 --> 00:16:03,300 doubled or I'm willing to 230 00:16:03,300 --> 00:16:04,000 terminate 231 00:16:06,900 --> 00:16:08,400 I'm going to just for the sake of this. 232 00:16:09,800 --> 00:16:13,900 and there's go call this to just, so we can see where that terminated 233 00:16:15,400 --> 00:16:19,600 So again, this makes the reasoning about the concurrency. Pretty simple. There's simple 234 00:16:19,600 --> 00:16:23,800 cases here one. One receive or one send on a 235 00:16:23,800 --> 00:16:27,800 channel on each line. There are these two particular instances. Once 236 00:16:27,800 --> 00:16:31,700 I'm in here, I'm going to do one of those two things otherwise I'm not. So let's 237 00:16:31,700 --> 00:16:33,800 just give this a run. 238 00:16:35,500 --> 00:16:39,100 And you see that in different cases, the two different 239 00:16:39,100 --> 00:16:43,900 terminations occurred. And if I add a return in here, 240 00:16:47,500 --> 00:16:51,300 then it'll be nice and neat. You see the things terminating. So this 241 00:16:51,300 --> 00:16:55,800 context idea, which Google came up with is a very neat way of passing 242 00:16:55,800 --> 00:16:59,800 things around, but underneath is just channels, it's just closing a channel for 243 00:17:00,900 --> 00:17:04,400 termination reasons and all it really does is prevent you accidentally 244 00:17:04,400 --> 00:17:08,900 causing close twice on a channel. There's another way to do 245 00:17:08,900 --> 00:17:11,900 that, of course, there is a lovely function called sink once. 246 00:17:13,200 --> 00:17:15,300 So, let's just take a look at that in the go doc. 247 00:17:17,000 --> 00:17:21,400 And sink once will call a particular function, 248 00:17:21,400 --> 00:17:25,400 exactly once. And so, you can use that to close a Channel. Once, 249 00:17:25,700 --> 00:17:29,800 although frankly, every time I've done it, it felt a bit like a hack. So I recommend 250 00:17:29,800 --> 00:17:31,000 doing something like this.