
Go concurrency patterns - joubert
http://talks.golang.org/2012/concurrency.slide#1
======
sirclueless
How do goroutines get cleaned up? For example, in many of these examples they
create a goroutine that is supposed to send a message over a blocking channel
eventually. What if I use their "select + time.After()" pattern and the
timeout hits and I return. Does the goroutine hang forever? Does it constitute
a memory leak? We both hold the channel, so it can't get garbage collected, is
Go smart enough to know there aren't any more readers and the goroutine +
channel can get cleaned up?

Maybe to solve this I use a "quit" channel as they do. Do I need to worry
about what happens if a quit message is never sent, or if two routines send
one, or if the receiver is multiplexed and only one of many readers gets the
message and cleans up correctly. It sounds like the whole malloc() and free()
dance all over again, except this time I don't have any nice invariants to
reason about such as "Everything that gets malloc()ed needs to be free()d,
exactly once". Instead I need to worry that everyone is playing nice and will
terminate when asked, _and_ I need to remember to ask exactly once.

~~~
biot
Rob Pike has a Q&A at the end of his video where he answers (what sounds like)
the exact same question you ask: <http://youtu.be/f6kdp27TYZs?t=46m17s>

Essentially, it gets garbage collected without you having to worry about it
unless you're doing something fairly special.

~~~
kfl
Except that Rob Pike later in the golang-nuts groups expand on exactly that
question: [https://groups.google.com/d/topic/golang-
nuts/bfuwmhbZHYw/di...](https://groups.google.com/d/topic/golang-
nuts/bfuwmhbZHYw/discussion)

Qoute: "Goroutines are not garbage-collected. They must return from the top-
level function (or panic) to have all their resources recovered."

------
Ezra
Video of rob giving the talk: www.youtube.com/watch?v=f6kdp27TYZs

Also, this makes use of the excellent "Golang Present Tool", which makes most
of the code on the slides executable.[1]

[1]: <http://godoc.org/code.google.com/p/go.talks/present>

------
duaneb
Completely off topic, but this is literally the worst presentation software
I've ever seen and I hate it when it pops up. It didn't fit in my screen, so I
zoomed out (or whatever the pinch movement does), at which point horizontal
scrolling stopped working, even when I zoomed back in. IIRC it didn't work at
all on the ipad.

Is it really so hard to have forward/back buttons on the slide somewhere?

~~~
kevingadd
Google presenters don't care about you unless you're running Chrome and have a
keyboard.

Pro-tip: If you load it with JS disabled, all the slides show up as plain
text. That might let you at least consume the slides' content.

EDIT: Pro-tip #2: It looks like if you tap the edge of the next/previous
slide, it should scroll onto screen. Kind of tricky, though.

------
ominous_prime
Here's a video of the talk from Google I/O 2012:
<http://www.youtube.com/watch?v=f6kdp27TYZs>

------
masklinn
> Rough analogy: writing to a file by name (process, Erlang) vs. writing to a
> file descriptor (channel, Go).

That analogy doesn't really work, messages can be sent to an Erlang process by
pid, how this pid came to be obtained isn't necessarily through naming (you
can also register a process to a name, in which case it is indeed like writing
to a file by name), and some of the things messages are sent to are not actual
processes (e.g. ports).

~~~
jlgreco
The way I see it is that you write to files with an fd, but and fd can point
to more than just files.

~~~
masklinn
> some of the things messages are sent to are not actual processes (e.g.
> ports).

~~~
jlgreco
Ah, fair enough. I don't know erlang. Can a process have multiple distinct
channels it receives on?

~~~
masklinn
Nope, each process has a single mailbox.

On the other hand, processes don't have to go through their mailbox
sequentially, they can prioritize messages (through pattern matching) and
handle these first even if they are the last message in the mailbox (it's
called a "selective receive"), so you get the same feature trivially by
tagging messages instead of sending a message to a different channel.

------
jws
Slide #33: _Fan-in using select_

I always get alternating Joe/Ann responses. I'm not seeing that in the code
though. It says selectors are chosen pseudo-randomly. I'm expecting Joe or Ann
to get a couple quickies in at some point.

~~~
Cyranix
If you added a trivial wait of random length before each response, mightn't
you see non-alternating behavior at times?

~~~
gizzlon
Not GP, but I also saw this.. Increasing the timeout to 0><10 seconds does
give the expected output..

But shouldn't I see this from time-to-time with 1 second as well? Hmm..

~~~
gizzlon
My rand.Intn() was always returning the same value. Seem like you have to to
seed it manually (once, in main() for example): _rand.Seed(
time.Now().UTC().UnixNano())_

[http://stackoverflow.com/questions/12321133/golang-random-
nu...](http://stackoverflow.com/questions/12321133/golang-random-number-
generator-how-to-seed-properly)

 _Edit:_ It's in the documentation, but you have to look for it.

" _Seed uses the provided seed value to initialize the generator to a
deterministic state. If Seed is not called, the generator behaves as if seeded
by Seed(1)_ "

------
jiggy2011
Ok, I've never actually written anything in go so humour me here.

The generally given reasons on HN that threads mapped 1:1 to pthreads are bad
seem to be as follows:

 _Shared mutable state is hard._

 _Memory usage is inefficient when you are allocating a fixed size stack per
thread and those threads spend most of their time blocked waiting for IO._

Let's assume you are using some thread pooling pattern , so thread startup
time is not such an issue.

Apart from perhaps better syntax with channels etc, how does go fundamentally
solve these problems in way that you cannot with standard threads?

For example, shared mutable state problems can be mitigated to a degree by
going down a "shared nothing" approach and handling shared state through some
middleman like a Queue or a SQL/NoSQL DB.

I assume go supports some form of pass by reference so you can still make
shared mutable state an issue with it if you pass a reference to a goroutine.

Each goroutine has it's own stack regardless of how many pthreads exist so
there can still be wasted memory on a blocking operation.

~~~
jerf
Nothing stops you from doing anything you want with threads, including using
them to implement very safe patterns. The problems are, 1: the thread
libraries do not afford those safe patterns, and indeed, afford very unsafe
ones (and also ones that compose poorly) 2: libraries written for the
ecosystem will end up using the poor patterns 3: lack of compiler enforcement
and how easy it is to accidentally mutate something unexpectedly mean that
unless you are superhumanly careful you will still screw something up,
somewhere.

And you are correct that Go does not enforce shared-nothing between the
goroutines. It has _better_ affordances on those fronts that conventional C,
but it is not _enforced_ as it is in Erlang or Haskell. And while I say the
affordances are "better", I still think that people screwing it up will be a
practical problem.

~~~
chubot
Yeah, this is exactly right... I have to say Go changed my thinking about
concurrency. But now I want to write a very small wrapper around pthreads that
lets you write in the actor style. It just adds those "affordances".

Go is more or less the actors style, except with the (discouraged) possibility
of sharing mutable state... even though for some reason it doesn't seem to be
advertised as such.

The reason is that I don't think Go can cover what C + Python can. C gives you
more low level control and Python is still shorter (and thus quicker). I like
Go a lot but I would rather program in C + Python (like I do now) than C +
Python + Go.

And then the other component to this is de-emphasizing the somewhat-horrible-
for-concurrency Python/C API. The library I'm talking about would have
channels, and you would have one end open in Python, and one end open in C.
Python and C are running in different threads. Rather than the crazy
subroutine/callback hell you have now with any nontrivial Python/C binding.

So basically I want to fix the C/Python interface, which is the only reason it
is awkward to program in C + Python (the languages themselves are both great),
rather than adding another language that overlaps highly with both of them.

The OS is written in C, so you've never going to get past C. If there was a
whole world written in Go, that might be reasonable... but I don't believe in
portability layers.

~~~
bcoates
What does the OS being written in C matter? libc has to make a syscall to
access OS functions just like anything else does. There's no obligation on a
language implementation to make calls through C. It's a thin enough layer that
if your language runtime is otherwise written in C you may as well, but that's
a design decision.

It would be perfectly reasonable to write a non-C language runtime targeting
Linux against syscall instructions or against Windows' documented system DLL
interfaces.

Even if it didn't there's no reason to ever add a C dependency to your system
if one of the languages you're already using has sufficient "systems"
versatility. Go is clearly intended to fill this role.

tl;dr: you can get past C just fine even on an OS written in C and the C
dependency isn't free.

~~~
rdtsc
I find this pretty interesting. It is basically using Erlang as an OS on top
of Xen. <http://erlangonxen.org/>

Haskell had a similar project but I can't remember what it was called.

~~~
dons
HaLVM. <https://github.com/GaloisInc/HaLVM>

------
RyanZAG
How do goroutines compare to things like futures in eg. java?

The ultimate structure seems much the same: start off a worker, and then you
wait for the result with a timeout. As an example, the following code gives
the same result:

In Go:

    
    
        c := make(chan Result)
        go func() { c <- Web(query) } ()
        go func() { c <- Image(query) } ()
        go func() { c <- Video(query) } ()
    
        for i := 0; i < 3; i++ {
            result := <-c
            results = append(results, result)
        }
    

In Java:

    
    
      ExecutorService executorService = Executors.newCachedThreadPool();
      Future[] futures = new Future[]{
       executorService.submit(new Web(query)),
       executorService.submit(new Image(query)),
       executorService.submit(new Video(query)),
      };
    		
      for (int i = 0; i < 3; i++) {
       Result result = futures[i].get();
       results.add(result);
      }

~~~
newobj
Find an example that shows using a timeout channel along with another async
activity on another channel, and then switching on that. Then you will start
to see some elegance versus the Java way. (Sorry, in a hurry otherwise I'd
provide the link myself :(

~~~
laureny
Please do show how to do this in Go when you have a few minutes, I'm really
curious (and happy to write it in Java once I see what you mean).

~~~
ciniglio
I think they mean something like:

    
    
        c = make(chan bool)
        go doSomeWork(c)
        select {
        case b := <- c:
            # do something here if something happened on the channel
        case <- time.After(5 * time.Second):
            # timed out :-(
        }

~~~
RyanZAG
Well the timeout can be handled in java too

    
    
      Result result = future.get(5, TimeUnit.Second);
      if (result == null)
         // do something if timed out
      else
         // do something with result
    

My original question was phrased badly though, and what I meant to ask was: is
the underlying implementation faster? Since Golang is designed as a systems
language, is the channel/gorouting system a lot more performant than the
Executor/future method of Java?

EDIT: I see what newobj is talking about now:

With the Java method, the 'task queue' in this case is very rigid and would be
hard to add futures from multiple locations, while the goroutine method allows
easy access to add new messages to the channel from anywhere.

You could probably emulate the goroutine method using a synchronized queue or
similar, but the goroutine version handles it automatically.

~~~
NateDad
FYI - you can also have 100,000 goroutines on a standard desktop without
hitting resource problems (the go authors mentioned debugging a system in
production that had 1.3 million). I doubt the same could be said for futures.

------
AndreasFrom
Off topic: Why are their slides so unusable on iPad!? I can't swipe to the
next slide without skipping that and 3 others.

~~~
mseepgood
The 'present' tool is open source, you can improve it if you have some
JavaScript skills:
[http://code.google.com/p/go/source/browse/?repo=talks#hg%2Fp...](http://code.google.com/p/go/source/browse/?repo=talks#hg%2Fpresent)

Here's the touch event handling code:
[http://code.google.com/p/go/source/browse/present/static/sli...](http://code.google.com/p/go/source/browse/present/static/slides.js?repo=talks#259)

~~~
enneff
Yes, someone, please do this. I am the author of present, but I just pulled
slides.js from another slide deck software.

It's on my list to try to fix this, but I have little experience with touch
devices. I'm sure someone with experience writing UI code for touch devices
could make quick work of this.

------
smosher
The phrase "concurrency patterns" tells me we are not yet talking about the
concurrency-oriented language of the future. This is in the sense that
patterns usually deal with the weak spots of the language, and lo, we must
build weird constructs like doing fanout (not relevant to the original
problem.) Go isn't alone here, in fact I see it every time someone thinks:
"Erlang is nice, but gee, PIDs are so darn crude."

In my mind, a language that makes concurrency the real priority (and I don't
mean to say Go, Rust, et al. shouldn't be tempered by other concerns) will not
require intermediate patterns to map the problem to the code. In other words,
something close to a 1:1 correspondence should exist between problem
concurrency and language support, including the nature of communications.

I'd like to draw a comparison to memory management in C. It's a far simpler
problem, but it's easy to illustrate that we don't (normally) need "allocation
patterns" that go beyond the problem of memory needs. You can sum it up: "If
you need memory, allocate it. Free it when you are done." The rules are simple
and the advanced memory allocation techniques aren't required to map to the
problem.

~~~
4ad
> patterns usually deal with the weak spots of the language

 _Design_ patterns, as the term is usually understood, do. Using the word
"pattern" to refer to organisational structures that arise in practice is
useful and distinct than preaching design patterns like some people do for
C++.

------
martinced
_"You don't need to be an expert! Much nicer than dealing with the minutiae of
parallelism (threads, semaphores, locks, barriers, etc.)"_

Great! But then, a few slides later:

 _"Go has 'sync' and 'sync/atomic' packages that provides mutexes, condition
variables, etc."_

: (

Can program written in Go still deadlock or not? I became a big of Clojure's
no-brainer "no locks at all, hence no deadlock" approach btw.

~~~
ominous_prime
Yes, you can still deadlock, but the runtime will usually panic saying that no
goroutines can advance, which is definitely nicer than just hanging. If the
core of your code is synchronized on channels, it becomes very easy to manage.

~~~
kibwen
How does the runtime detect this? I'd have suspected that such a thing would
be undecidable, but I'm not at all a concurrency expert.

~~~
jamesmiller5
The run-time checks if all goroutines are asleep and waiting for input. If
true it panics and halts the program.

It can't detect live-lock or thrashing, if you are still waking up other
goroutines but not doing anything useful it won't prevent that.

