Google I/O 2013 - Advanced Go Concurrency Patterns

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

6:30 "recently my favorite feed reader disappeared"

nice jab at those assholes at Google... oh wait heh

RIP Reader :(

👍︎︎ 8 👤︎︎ u/BigKRex 📅︎︎ May 19 2013 🗫︎ replies

Where is the source code for the example RSS client in the talk?

Does anyone have a link?

👍︎︎ 2 👤︎︎ u/octonon 📅︎︎ May 19 2013 🗫︎ replies

So the talk that wasn't streamed is now on Youtube, but I still can't find "Fireside Chat with the Go Team" which was streamed :/

👍︎︎ 2 👤︎︎ u/[deleted] 📅︎︎ May 18 2013 🗫︎ replies
Captions
SAMEER AJMANI: Hello. My name is Sameer Ajmani, and my job at Google is to help make Go programs work well in production. And I'm going to talk to you today about some of the code structures that we've developed to make our code more responsive, to make it clean up after itself properly and respect its resources, and to make it easier to read and maintain. And I'm one of the members of the team driving adoption of Go within Google. So this is an advanced talk. What this means, I'm going to throw a lot of code at you. It's very dense. And it shows you a lot of the different ways you can put Go's concurrency primitives together to build interesting structures and get your code to respond well to a lot of things happening at once. So get ready. One of the critical things to notice is that Go supports concurrency in the language, not as a library. That means you have great syntax for dealing with starting concurrent events, communicating with them, and then reacting to that communication. And that syntax is going to come out in the code samples in this talk. And I hope to make the point that it really helps you read and understand these programs that are dealing with a lot of events. By means of quick review, the main primitives are Goroutines and Channels. Goroutines allow you to start functions executing in the same address space alongside the existing one. So this first called go f starts the function f executing with no arguments in a new Goroutine. The second one starts function g with parameters 1 and 2. And the Goroutine that called these two-- made these two statements, these Go statements, continues executing, continues running. Now, Goroutines need to communicate and synchronize so that they can understand what each other's doing and you can gather results from other Goroutines that are executing. And for that, we use Channels. Channels are typed conduits that provide-- they both provide a way to pass data between Goroutines and a way for the Goroutines to find out what state they're in in terms of what point in their execution they are. That synchronization is critical to a lot of the structures we will get into today. So in this example, I've created a channel of integers, assigned it to variable c. And I started a Goroutine. Here I'm using an anonymous function closure that's going to send the value 3 on that channel of integers. And that Send operation is going to block until some other Goroutine is ready to receive from that channel. And then the final statement is going to receive off of the channel c. It's going to block until some Goroutine sends its value, and then store that value in the new variable n. So n is going to be 3 after end of this. For a lot more on these basics, see Rob Pike's talk from last year at I/O. I'll provide a link at the end. Here's an example that puts these primitives together. What it's going to do is print out ping and pong on alternating lines and an incrementing count on each line. The way this works is we have a data type, ball, that's going to keep track of how many times it's been hit. And in our main function, we're going to create a channel of pointers to ball. So we're going to pass a pointer to the same ball instance, back and forth, between Goroutines. We're going to start two Goroutines that each run the function player. We'll pass one with the name ping, the other pong, and both references to the same channel. We'll start the game by sending a ball on that channel and allow the players to pick it up. One player will receive the ball and then send it back to the other. We'll sleep for a second, and then snatch the ball off the table to end the game. The player function here is in a loop. It's receiving the ball, incrementing the number of hits, printing that out, sleeping briefly, and then sending the ball back. So again here's that ping-pong exchange. Now, one of big benefits of having this concurrency managed by the GoRun time is we can do things like detect when the system is deadlocked. So if we never send the ball on the channel, we'll do that one-second sleep, and then we'll get a fatal error. It says all Goroutines are sleep deadlock. And it'll print out the stack traces of the Goroutines that are sitting in the system. We have our main that's blocked on channel receive, that's received out of the table here. And we have our two players, main.player, that are these two Goroutines, and you can see they're also each stuck on a channel receive. This is very useful to understand when your system gets deadlocked. And that set of stack traces can be useful for other things as well. So a Panic in Goroutine is going to dump the stacks. It's at the top of your program. And there are a number of other ways to get those stack traces. The runtime package provides ways to get at it. And there's even ways to export it through HTTP handlers. So you can look at, say, a running server, and see where its Goroutine stacks are. So if I run that Panic, right at the end of our program, we get a dump of the stacks again. And we see our main Goroutine, which we expect. But we also see these two players are hanging around. Those Goroutines are still existing in the system. They haven't exited. And it's kind of obvious from the code. They are in an infinite loop. Why would they exit? That's a leak. These Goroutines are going to continue sitting in the system. And we want them to exit when they're no longer needed. And that's really what this talk is about. When you're building long-lived systems, like the one in Google, you need to be careful to make sure that you get rid of the things you no longer need. And Goroutines are one of those things. They need to clean up properly. So we're going to look at programs that handle communication, but also various periodic events in time, and then cancellation, deciding when things are done and making sure that we clean up properly. The key mechanism that's going to let us do this is Go's Select statement. And this is sort of the third leg of its concurrency support. A Select statement looks a little like a switch, but it's fundamentally different. A Select blocks until one of its cases can proceed, and each case is a communication. So in this example, the first case in the select attempts to send the value x on the channel xc. And the second attempts to receive a value from the channel yc and store it in the new variable y. And the select will block until exactly one of these cases can proceed, which means that communication happened. Then the body of each Select case can take action on that event. In particular, the receive off the y-case can use the new value received from y. And we know that that send on x didn't happen. So the motivating example here-- to put this all together-- is a feed reader. Recently my favorite feed reader disappeared. So I said, well, I need a new one. Well, we know Go. So why not just write one? But where do we start? I don't know much about feed readers. I know they have something to do with RSS. So we can open up this website, godoc.org, which is pretty fantastic. It provides automatically generated documentation for the various Go projects-- BitBucket, GetHub, Google Hosting, and LaunchPad. And so if you search for RSS here, you will get a number of Go packages. We get their tag lines here that says what the package is about. A number of these are just providing Go data structures that correspond to the RSS data types. But some of them actually provide clients. And that's what I really want. So we can click through on a given package and get more information on what the package owner has described. And this is all powered by godoc.org. So I chose a package and started looking at its interface. What it provides is a fetch function, something like this. It takes a URI from which to fetch the feed, and it returns to me a slice of items and a time, which is the next time I should try a fetch on this particular feed. Or if there's a failure, it'll return the error value. The errors are returned. And I don't get the items or the time. And in this example, I'm going to strip down. And I've just given us some basic elements like the title, the name of the channel, and the unique ID. But, of course, the real RSS items have a lot more data. But this isn't what I want. I want a stream of items that I can then build, say, UI on top. So what I really want is a channel of items. And here, this little arrow hanging off the channel type says that it's a receive only channel. I just want to suck on that channel and do interesting things with the items I get. Also multiple subscriptions, not just one. So let's take a look in more detail about what we want. Here's what we have. We have this thing that we can call fetch on periodically to get items. And we're going to put in an interface so it makes it easy to provide a fake implementation for this talk. And you can imagine using it for testing. We have our function, which given our domain, a blogger domain, is going to give us a fetcher implementation. And that's where we're starting. What we want to build is something more like a subscription that gives us a channel on which we can receive these items and gives us a way to say we're no longer interested-- unsubscribe. So we're going to close that subscription. It's going to give us an error if there was any problems fetching that stream. And this gets to that whole cancellation point. We want to be able to clean up when we're no longer interested in something. We have a subscribe function, which given a fetcher, transforms it into a subscription. It takes this idea of periodically fetching and gives us this stream. And then we have a merge, which takes a variadic list of subscriptions, any number of subscriptions, and gives us a merged one, one whose series of items are merged from the various domains. Here's an example that puts it together. This is using a fake fetcher. So we're just getting which =gs we're fetching from and some item number. Then the panic at the end is the same technique we used before. It just shows us that all that's hanging around is the main Goroutine and a syscall Goroutine managed by the runtime. There's nothing with regards to subscription that's still handing around. So we've cleaned up is the point. The code here, we are making a call to merge, and we're passing it three subscriptions. Each subscription is fetching a different domain. And that's our merged subscription. We're going to start a timer, which is after 3 seconds close the subscription, because otherwise, we're going to sit here, reading these all day. And then we're going to iterate over this channel. Now, here I'm using Go's range function, range built-in, which allows you to iterate over things like slices and maps. And on a channel, it means continue receiving over that channel until the channel is closed. And closed is a built-in operation you can do on a channel to indicate to receivers that no more items are coming. So really, the main reason to use close, in my opinion, is to make these four loops really nice. It makes it very easy then to write things that are easy to reason about. And then we're going to print each item and then dump the stack. So again, this is a randomized thing. We're getting different items from different blogs at different times, different next-fetch times. Most of this talk is going to focus on subscribe. That's all we have time to go into. We'll provide the code for things like merge online later. The idea behind subscribe is to translate this periodic fetch into a continuous stream of items. The way that's going to work is we're going to have an un-exporting data type that implements subscription. And that's sub. Sub is going to contain a fetcher. And it's going to have this channel of items it exposes to the client. And when we create a new subscription, we're going to initialize our subtype. And we're going to start a Goroutine called loop. And this is the key bit. This is going to implement that periodic fetching, but also handle events like closing and delivering items to the user. We will see how we put that all together. Again, subscription is an interface. And what that means in Go is we need to implement all the methods in that method set. So we have two to implement. One is updates. And that's easy. We have our update channel in sub, and we are just going to return that to the user. Note that we're returning the receive-only view on that channel here. So the client of the subscription is only going to be able to receive. They won't be able to try to send on that channel. And Close has two jobs. It's got to make that loop Goroutine exit. It has to find out about any fetch error that happened while we're trying to get that subscription. So what does this loop Goroutine do? It's got three jobs-- call fetch periodically, deliver items on that updates channel, and then when Close is called, exit and report the error. This doesn't sound too hard. So here's the implementation that you might just try first. Let's have a loop. And if we're closed, let's close and update channel on return. Otherwise, we run our fetch, we get some items. If we get an error, sleep for a little while, and loop around again. If we succeeded, deliver each item on the updates channel and then calculate what's the next time we're supposed to fetch, sleep for that long, loop around again. And close simply sets this Boolean to make the loop exit. And then reads the error off. If we run that, it seems to kind of work. We seem to be getting items. And it looks like it even might have exited cleanly in this case. But it turns out this code is very buggy. And I'll go into exactly why. The first bug is that this closed a bit, and this error value are being accessed by two Goroutines with no synchronization. And this is bad. This means that this is a data race. You can see-- particularly in the case of the error, which is an interface value, you can see partially written types, if there really are because these are executing in a shared address space. So we have to resolve that race. And you notice this race is sort of by inspection, which isn't a very satisfying way to find data races. Luckily with Go 1.1, we have a new race detector. When you go run your program, you can pass this hyphen race flag on your Goroutine. and you can get a nice report. So we're going to run this with a race detector-- hopefully. There it is. And we get an error that says there's a data race. There's a read in the Goroutine called loop. And it was a previous write to that value from the Goroutine that ran Close. And it even tells us where these respective Goroutine were created. And then we see in our stacked race it came below. So the race detector is a really great way in a program that is doing a lot of concurrency, that's dealing with a lot of Goroutines and a lot of data, to discover when you're not synchronizing improperly. I'm going to talk about how you fix these data races. The second bug is a little more subtle. This is really a resource leak of an insidious sort, which is our loop is just sleeping when it has nothing else to do. And what that means is it's not responsive when it's time to close a subscription. Now, this might not be a big deal. I mean, it's just one Goroutine hanging around. We know it's going to exit eventually. It'll see Closed. But what if this next time is tomorrow? Some feeds are updated fairly rarely. So this could be hanging around for quite a long time. We want to be in the case that as soon as the user calls Close, we clean up, because there's no reason for this to hang around. The third bug is, in a sense, the hardest to catch. As I said, sends will block until there's a Goroutine ready to receive the value. So what happens when the client of the subscription calls Close and then stops using the subscription? There's no reason they should keep receiving off of it. Well, this send will block indefinitely. There's no one to receive it. Nothing's going to happen. This loop Goroutine is going to stay hanging around in the system forever. So luckily, we have a way to fix all of these problems by refactoring the way our loop works. And what's interesting-- so it's going to be built on this select statement in Go. And the point I want to make here is that in this case, we have a type that wants to consider any of these three things could happen-- Close was called, or it's time to call Fetch, or it's time to deliver an item on updates. Select is what lets you do that. To consider multiple things that could happen at the same time, when one happens, take action on it, then consider them all again. The basic structure is what I call for-select loop. So loop is going to run in its own Goroutine. And at the top of it, we're going to declare a mutable state, and that mutable state is owned by that Goroutine. And in our loop, at the top of the loop, we're going to set up the channels to use in our select And then our select is going to consider these the various cases. And then they are going to manipulate that state that's owned by the loop. Then we're going to run around the loop again. And the key here is that there's no data races because this is a single Goroutine. It's a straight-line code. It's much more algorithmic than you might be used to in dealing with threads and event-driven style. I'll show you how powerful this is and the way we resolve these errors and then build on this structure. So we're going to go case by case. We have three cases. The first one is Close. We need to transform that closed Boolean and that error value into communication. And we're going to do this by introducing a channel in our subscriber implementation called closing. And this is a funny signature. It's a chan of chan of error. It's a channel on which you pass a channel. So what's going on here? This is a request response structure. So the loop you can think of as a little server that's listening for requests on this closing channel, in particular, requests to close. And when we call Close, we're going to say, hey, loop, please close. And we're going to wait until the loop acknowledges that by sending us back the error if there's been an error on fetch, or nil if there's been no error. And this request response structure can be built up quite a bit. There's some material I'll point out later that talks about this in more detail. So the code here is that Close now creates the channel which it wants to receive an errors, sends that to the loop, and then waits for the error to come back. In loop, we have-- this is just a snippet of it-- our first select case, which is if we receive a value on closing, deliver the error to the listener, close the updates channel, and then return. And this error is the one that fetch is going to write to. And fetch is next. So this is a big case. And I'll go into this in some detail. The state here is what's returned by fetch and shared with the other cases. In particular, we have a set of items that we fetched previously and that we want to deliver to the client. We have this next time that we need to run our next fetch. And we have the error from a failed fetch. At the top of our loop, we have to calculate when is our next fetch? And so that's this fetch delay. Initially, it's going to be 0. So our first fetch is going to be immediately. This start fetch is a channel. We use the time.after function in the time package to create a channel that will deliver a value exactly after fetch delay elapses. And this second select case is going to be able to proceed after that time has elapsed. In this case, we're going to go ahead and fetch our items. We'll run fetch. If there's an error, we'll schedule our next fetch to be 10 seconds from now. You can imagine backing off there. And if we succeeded, we'll append the fetched items to our pending queue. Our last case is going to then deliver the items in those pending queue to the client of the type. And this seems pretty easy. We should be able to just take the first item off pending and deliver it on the updates channel and then advance the slice, reslice it to be one forward. Unfortunately, this crashes exactly when pending is empty. And this is because the way select is evaluated, it evaluates the expressions in the communication and it evaluates pending sub 0 when it runs a select. And if it's empty, that's going to be a panic. We're going to fix this using another technique. And it's going to combine two properties of the Go language that are somewhat interesting. First is that nil channels, if you send or receive on an nil channel, it blocks. It doesn't panic. It just blocks. And we know that selects will never select a blocking case. It will only select cases that proceed. If you put this together, we can use the fact that we can set a channel to nil to disable a case that we don't need this time around. And this replaces a lot more complicated logic where you have multiple different selects depending on what can happen. We have one select where you can turn certain cases off that you don't need. So our example here, just a simple example to demonstrate this. We're going to create two channels of strings, a and b. Start two Goroutines that deliver a to a and b to b. We're going to flip a coin and set one of them to nil. And then select to see which one delivers a value. And what you'll see is that each time we run it, we get a value exactly from the one that wasn't set to nil. It did show up there. Yeah, there's a. The way we are going to fix this now is in our select case, we're only going to attempt to send if we've got a value to send. So we're going to create an updates channel that's nil. And we're going to set it to non-nil exactly when you have something to send. And then we're going to set our first item to be pending sub 0. So that fixes this issue. And now we can put all three cases together. So we have a case that's closing. And in that case, we're going to send the error to the user and exit. We have start fetch, which means it's time to run the fetch and append the pending queue. And then we have the updates. And we advance our queue. And the key thing here is that these three cases are interacting with the local state and loop. There's no races. This is all straight-line code looking around. But you're doing some fairly complex updates to your state because you're dealing with a lot of things going on. And yet, this is still responsive. When it's time to send something to the user or the user's no longer interested, we clean up very rapidly. And this is key to building responsive systems. In all of this code, there's no locks, no conditioned variables, no callbacks, none of the typical stuff you deal with in current programming. It's really the syntax and the structure that Go gives you that makes this possible. We fixed our three bugs. We no longer have the data race on closed and error. Because now we're using communication instead. We're no longer blocking for a run sleep because it's just one of the cases on our select. And we're no longer blocking forever an update, again, because it's just one of the cases in our select. We're always responsive to any of these events happening. Now that we have this structure, we can make a number of improvements. It's much easier to see what's going on in this data type. The first issue we noticed is that this fetch call can return duplicates. When you fetch an RSS feed and you re-fetch it, you may get many of the items that got last time as well as a few new ones. We can fix this trivially now inside loop by just keeping track of the ones we've already seen because we don't want to deliver duplicate items on our stream. We want to have a deduped stream. So we have a set of the items we've seen represented as a map of strings to bool. And then instead of just appending all the items we've fetched, we iterate over those items, check whether we've seen it, and only append the ones we need. That's all we need to do to dedupe. A second issue is that the set of pending items can grow without bound, because our downstream receiver may be distracted, may not be keeping up with the items were offering. And you can think of this as back pressure, right? If the receiver is doing something else, we don't want to keep acquiring more and more pending items and allocating an arbitrary amount of memory. So what we want to do here is bound. When the receiver is slow, we want to stop fetching new items. And we can use that nil channel trick again. So remember that start fetch schedule told us when the next time we should fetch is. If we leave that to nil, we won't run another fetch. So we're going to decide, in this case, that the maximum number of pending items we have is 10. And we're only going to schedule the next fetch if we have room in our pending queue. It's a very simplistic policy. You can imagine doing a variety of things here. You might care only about having the most recent items since you could drop stuff from the head of pending instead. So you can think of this as a node in a data stream where you can implement policies on how you want to deal with a queue overflow. Finally, we have this issue that fetches are doing IO. They're talking to remote servers, and they could take a long time, and so this particular call may block. And we may not want to block our loop on these fetches. We want to remain responsive. If the server is taking 10 seconds to respond and the user calls Close, we want to be able to ditch and move on. And the way we're going to do is we're going to move fetch to its own Goroutine. But now we have to figure out when that fetch finishes. We need to then resynchronize back with what loop is doing. We'll do this by introducing another case into our select and another channel for running fetches to communicate with us. Here's how that works. First, we need a channel on which select can send its results. We're going to define a fetch result data type, which is a struct containing the fetched items the next time that error. We have this fetchDone channel, which is channel fetchResults. And our invariant. this is non-nil exactly when fetch is running. So we want to start a fetch when fetchDone is nil. That's our initial state. So we'll go ahead and start a fetch. And the startFetch case is going to set our fetchDone channel and start a Goroutine that runs our fetch. And that Goroutine is going to run a fetch and send the results on that channel. And then the case exists. And so we loop back around. And we are considering closing and delivering items on our updates channel. When the fetch finishes, it's going to deliver its result on this channel. And that's just the rest of the fetch case that we saw before. And this is a fairly small transformation to what we had before. And we made our loop not block on the IO that's going on. And we still synchronize properly with all the other events that can happen in this subscription. So we've implemented subscribe. This code is responsive to various events in the environment. It cleans up everything and handles cancellation properly. And the structure that we introduced made it relatively easy to read and change the code. The key techniques were this for-select loop, which allows you to consider all the things that could be happening to your type in time. And this idea of a request-response channel. And you can really build quite a lot on that, where I think of Goroutines in Go, you can have them acting as servers and clients on a little distributed system that you own. And you can use these channels to exchange data. And you build a lot of the same structures you'll see in distributed systems within your program. And finally, we have this technique of setting channel to nil and select cases to turn cases on and off. That really just helps you simplify some of the logic when you're dealing with a staple type like this. There are going to be a lot more details online. All the code will be online, including merge, which is also interesting because it's dealing with multiple subscriptions merged onto a single channel. Concurrent programming can be very tricky, and clearly the code that I presented here is relatively complex. But the point is that Go really makes this easier by giving you good building blocks and good syntax. Channels are pretty versatile. They're not just for conveying data, but also for events in time and things like cancellation signals. And you can use Goroutine to serialize access to a mutable state in a way that's much easier to understand than traditional synchronization primitives. We also have great new tools. We have the stack traces and the deadlock detector I showed you earlier. The deadlock detector is particularly handy when you're running unit tests. When you're testing a data type in isolation, and everything sort of seizes up, you can see what goes wrong. And we have the race detector which is great for larger tests and integration tests and larger programs to see whether things are working right at scale when you've got lots of Goroutine exchanging data and accessing state. There's a lot more information about this online. Last year's talk, Go Concurrency Patterns, goes over the basics and a lot of the basic structures you can build with Go's concurrency primitives. And Concurrency is not parallelism talk, considering the difference between how you structure your code to deal with a lot of events and what it means for things to actually run in parallel at the same time. And that distinction can be a little subtle. And it's interesting because Go really lets you write your code differently. You structure your code differently when you want to be responsive and deal with a lot of things happening at once. There's also a code walk that goes over some of these basics as well. The Go tour gets into this. It starts you from the basics and works all the way through the concurrency primitives. And I also plug the Go Team Fireside Chat, which is next, in Room 2. And that'll have the whole Go Team answer your questions about the language. Thank you. Questions? AUDIENCE: Hi. In one of your early examples, you were leaking Goroutines, right? SAMEER AJMANI: Yes. AUDIENCE: I'm wondering what the best way to-- if there's tooling or the best way to detect when that's happening? SAMEER AJMANI: It's a very good question. I think there is some tooling to be built here to try and figure that out. For now, I'm using relatively primitive techniques by actually inspecting the set of stack races that are available and frankly that can be very effective, particularly in tests and in isolation. I think it would be interesting to see if we can automate this a little more. It's a little bit hard to really detect when-- well, we know it's hard to detect when code is done executing, right? AUDIENCE: Yeah. SAMEER AJMANI: So-- sorry? AUDIENCE: Blocking profiles are in 1.1. SAMEER AJMANI: Blocking profiles? AUDIENCE: Yes, so you can actually get a graph of what makes them block. SAMEER AJMANI: Blocking profiles are in 1.1. So you can get a graph of what's blocked. This is apparently-- we should have put that in the talk. AUDIENCE: Thank you. SAMEER AJMANI: Thank you. Yes? AUDIENCE: I guess I have a very similar question. About halfway through the talk here, one of the things you did was surprise us all with three errors that we didn't expect. So there are lots of languages out there that can do concurrent programming. What I want to know is how Go helps me find those bugs which I'm guaranteed to write? SAMEER AJMANI: The race detector and the unlock detectors are good basic things. But I think, again, there's a lot of things we can do moving forward to help detect these more automatically. Our static analysis tools are getting better and better as well as our runtime is also developing. Part of it also is experience with the language. Because when I wrote this, I had to convince myself that-- once you've written code that's dealing with multiple Goroutines, this kind of race, I think, between closed and error is apparent. But you're right that it's not obvious to new programmers. And part of it's having the proper tools. Part of it is changing the way you think about how these Goroutines work together. It's key-- the tag line I remember is share memory by communicating, not communicate by sharing memory. And here, we're mutating shared memory without any synchronization. We fix that by converting it to communication. And that's key. AUDIENCE: You're convinced that over time, this language will allow those static analysis tools to be developed? SAMEER AJMANI: I'm convinced that our tools will get better and better, yes. That doesn't mean that we can detect every race. But I think data races are certainly one of the most insidious and hardest things to debug. The race detector is a really great tool for helping us detect that. And I think clearly we have to build our collective ability to write programs that take advantage of our hardware and be very responsive to the events and types. And concurrent programming is important. We need to build the tools up. And I think Go is a great base on which to build that because I believe the syntax really helps you read and understand the code. And we can build up from there. AUDIENCE: Thank you. I was at the previous talk. And it was about App Engine and Go. And it highly recommended using Go and App Engine. Now, all these debugging tools, will they work in App Engine, meaning the local development environment of App Engine? Or how do we debug App Engine Go apps? SAMEER AJMANI: How do we debug App Engine in Go? Next session. That's a great question for the Fireside Chat. Thank you. AUDIENCE: Hi, there. I'm very new to Go. But I've been studying some concurrency structures and other languages. And it seems like one of the popular approaches, specifically on the JVM especially now, is to use actors. And the way those approach concurrency is by kind of holding their hands up and saying, no. No one can touch this message queue except the object that it's assigned to. And it seems like Goroutine and Channels are similar to the approach, except that the channels can be touched by external sources. SAMEER AJMANI: It's a great question. It's really interesting because I think the difference here is that you can use Goroutines and channels to build something actor-like, which is sort of what I did here with the four select group if you squint. But you can also use them to do a variety of other things. If you look at Rob's earlier talk, you'll see there's a number of ways you can use it. And there's certainly value in the approaches these other concurrent languages have done. And the fact that you can, in a fairly straightforward way, convert those good approaches in Go is a strength of the language. So yeah, certainly it's the case that serializing access to mutable state, which is what actors do as well, is great. So do critical sections. And so the question is, what's the way that allows you to structure code in a way that is easy to reason about, easy to maintain? I presented one here. And there's a number in the other talks and links I've provided. AUDIENCE: OK. Great. Thank you. SAMEER AJMANI: Thank you all very much.
Info
Channel: Google Developers
Views: 161,167
Rating: 4.9243636 out of 5
Keywords: gdl, i-o, cloud
Id: QDDwwePbDtw
Channel Id: undefined
Length: 34min 10sec (2050 seconds)
Published: Sat May 18 2013
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.