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.
6:30 "recently my favorite feed reader disappeared"
nice jab at those assholes at Google... oh wait heh
RIP Reader :(
Where is the source code for the example RSS client in the talk?
Does anyone have a link?
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 :/