Go concurrency patterns
talks.golang.org
talks.golang.org
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.
If you choose to select indefinitely, yes, you will need another channel to signal when you should return, or the goroutine will continue to run, and continue to hold a reference to that first channel.
A concrete example, slide 34 [1] contains this code:
func main() {
c := boring("Joe")
for {
select {
case s := <-c:
fmt.Println(s)
case <-time.After(1 * time.Second):
fmt.Println("You're too slow.")
return
}
}
}
This is code that looks really simple, but it's only correct because the process is known to terminate. You couldn't, for example, have the body of this function as a subroutine somewhere: every time you execute it a boring() goroutine is going to be left hanging, which is a classic memory leak. What's worse, "c" looks like an average variable I don't need to worry about (yay garbage collection!) but it's not because it escapes to a running goroutine.Correct me if I'm wrong but even if boring() was a much simpler function that returned after generating one value, if the timeout happened first it would be left hanging. In that case the only thing that would make the program correct is that of two racing goroutines, one is expected to return first. That's the kind of thing that keeps me up at night, especially when Go makes it so easy to treat goroutines and channels like every other GC-managed data structure when in fact they are every bit as leaky and difficult as threads.
Such is the case of the time.After() function, which easily solves the problem by returning a buffered channel. That would not work for an infinite generator of course.
The power and orthogonality of Go's concurrency primitives is actually encouraging. I'm not picking holes in it because I think Go is a bad language. On the contrary, I just think that it's not the endgame of concurrent programming. Using channels is not a safe operation in the same way that passing around memory references in a GC-managed language is safe, or passing values in a type-safe language is safe. You can write a correct, concurrent program with pretty much any behavior you want in Go, but it's still a minefield of deadlocks and memory leaks. I think there's room for improvement there.
(By open mind, I mean that I acknowledge that I have years of being steeped in an asynchronous message passing environment and that I may need to unlearn some things and learn others. I'm a polyglot programmer, so this isn't my first time for that, either. There are some some useful things that Go's channel style is easier to work with than Erlang's style. Still, right now I'm cautiously negative on the net utility of that, even so.)
I've deadlocked Erlang twice in many years. It didn't make it to production either time. I deadlocked Go twice in the first two days, and several times since. (And Go terminates the entire process upon deadlock.) And what really concerns me in terms of recommending it for work is that while I instantly understood how I deadlocked Go, it was only because of the fact that I'm fairly experienced now in reasoning with these sorts of advanced concurrency constructs. Many of my coworkers would have been stuck for hours.
It's not out of the running yet, considering many of its positive qualities and the competition it is up against, but the Unambiguous Win I was hoping for is not emerging. (Many of these issues may be avoidable with style; for instance, one can write goroutines that manage resources in a fairly Erlang-y style and which are services that have a continuous receive-reply loop, and those are safe enough. But I'd really rather have more compiler support here rather than depending on convention; that's how we got to the realization that conventional threads are a bad idea in the first place.)
My start with go was just like yours, but it quickly started to "click", and more robust patterns began emerging more naturally for me. I don't think I can verbalize yet what has made the difference for me, but it may something like "structure your code around channels" vs "use channels to synchronize your code".
When I first started with golang I too was flailing around with deadlocks, but once you have spent a bit of time with it, it becomes obvious when you have messed up.
I think that this is more of a lack of familiarity with the language rather than an intrinsic problem with golang.
That is exactly what I can't get over. I don't understand why this is the default. Most processes in the real world are not synchronous. Sending an asynchronous message makes more sense as a primitive (synchronous can just be modeled on top as a mini protocol, sender sends his own address). The other way is not as clear. (Spawn a separate concurrency primitive and send to the channel from there?).
The other thing that bothers me is the focus on channels rather than on processes. The two can be seen as equivalent but for some reason process-centric design seems more natural to me.
There is of course Scala with Akka but I don't see JVM a benefit, I see it as a nuisance (it is more of an irrational political thing probably come to think of it).
...
ch := make(chan int)
defer close(ch) //I can timeout later if I want
go consumer(ch)
...
//I timeout down here and return
}
func consumer(input chan int) { //I will terminate when the input channel is closed
for x := range input {
fmt.Printf("I got %d!\n", x)
}
} func b(chan x) { time.Sleep(60 seconds); x <- true }
func a() {
x := make(chan bool)
t := timeoutChannelCruft(30 seconds) // don't remember the syntax
go b(x)
select {
case <-x: return;
case <-t: return;
}
}
What happens to goroutine b and channel x? Does goroutine b block for the remainder of the program? Does channel x ever get cleaned up?I'd guess b() blocks forever so in your timeout case you could probably just do
go func(){ <- x }()
to make sure that the b() eventually returns and is cleaned up.This is kind of my point: with Go's concurrency, all the contracts are implicit and dangerous, exactly the kind of thing that are supposed to be solved by a good type system and garbage collection (incidentally, Go gets all of that stuff right).
- Goroutines are not garbage collected.
- In this case and many others, you can just use a buffered channel. But of course in many other cases it doesn't solve the problem.
- If you solve this with a "quit" channel but don't use it, obviously you have a problem
- Several goroutines can try to send to the same "quit" channel. Make it buffered and have every goroutine try to send.
- In the case of several listeners, you can just close the channel and all receivers will return.
- However if you have several senders and several receivers on the "quit" channel, then you have a problem: closing a closed channel will panic.
See also:
https://groups.google.com/forum/?fromgroups=#!msg/golang-nut...
https://groups.google.com/forum/?fromgroups=#!msg/golang-nut...
Essentially, it gets garbage collected without you having to worry about it unless you're doing something fairly special.
Qoute: "Goroutines are not garbage-collected. They must return from the top-level function (or panic) to have all their resources recovered."
Also, this makes use of the excellent "Golang Present Tool", which makes most of the code on the slides executable.[1]
Is it really so hard to have forward/back buttons on the slide somewhere?
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.
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).
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.
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.
But shouldn't I see this from time-to-time with 1 second as well? Hmm..
http://stackoverflow.com/questions/12321133/golang-random-nu...
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)"
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.
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.
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.
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.
Probably Forth could be thought of an alternative non-C stack, and of course it's used on machines without an OS. But that's how far afield you have to go to get away from C.
My point is that when you're using Go your stack is still Go and C, to an extent. Usually you won't need to peek under the hood, but at some point when developing nontrivial services, you always need to. Basically my philosophy is that you should always understand at least 1 abstraction level below what you program in. With either Go or Python, that's C.
Haskell had a similar project but I can't remember what it was called.
You can solve the same problems by manually using threads, message queues, etc. but it would be much easier if the tools and language already had generalized solutions to put into practice.
What go does is make some things easier, goroutines are multiplexed onto OS threads in a way that means you don't need to worry about pooling or scheduling. Channels provide an easy way of sending and receiving data in either a blocking or non-blocking manner
In go things are generally passed by value as a default, if you want to pass by reference you just pass a pointer.
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);
} 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 :-(
} 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.
[1]: http://docs.oracle.com/javase/7/docs/api/java/util/concurren...
It is up to executorService to decide how many threads to use to execute those tasks. But they will be executed in threads. So you are unlikely to have more than a few hundred(maybe a few thousand?) of them around at any time.
But looking at your code; c := make(chan Result) is several orders of magnitude nicer than ExecutorService executorService = Executors.newCachedThreadPool(); =)
I don't know why people feel they need to break regular scrolling for their slides. I'm sure it's interesting content, but I'll wait for a PDF version of the slides.
When you're making the basic task of seeing the slides impossible, nobody cares about your pointless turd-polishing.
Furthermore, PDF readers (at least Adobe's, but third-parties also sometimes do even if not all APIs are present) embed an ECMAScript interpreter, so you can indeed have embedded runnable code snippets in PDF: http://www.adobe.com/devnet/acrobat/javascript.html
These are slides for the presenter. Nobody cares if you see them or not. Only the presenter has to care.
> PDF readers [...] embed an ECMAScript interpreter
So you wrote a Go to Javascript compiler? Can the VM running in the PDF reader implement a web server that can interact with the outside world?
Irrelevant, the whole thread of inquiry is about seeing the slides locally, you're two comments too late to try that one: you implicitly acknowledged the desire and desirability of personally viewing the slides in your previous comment.
> So you wrote a Go to Javascript compiler?
Also irrelevant (although there likely is no need to, there's an LLVM Go frontend and there's emscripten), the exact same method used here can be used there.
> Can the VM running in the PDF reader implement a web server that can interact with the outside world?
That doesn't even remotely make sense.
It could be a lot worse, they could be like 99% of the other slides in the industry: completely worthless without listening to the accompanying lecture.
I'm in a desktop browser, and I find this incredibly annoying to navigate.
If that's not "primary usage" for viewing slides posted online, I don't know what is.
Seriously? The primary usage is presentation..........
Asking that the presenter dedicate less time to preparation and more time to constructing some sort of elaborate LLVM/emscripten PDF presentation just for the benefit of secondary users who won't/can't use arrow keys is fairly unreasonable.
That's a joke right?
> You are asking
I'm not really asking anything, I merely replied to "4ad"'s insulting dismissal of concerns voiced by two other people as to the usability of the deck, then replied to his dishonesty and goalpost-moving antics.
> minor facilitate secondary usage
Making it actually possible to see the deck you officially posted online is neither facilitation or minor.
> at the expense of primary usage.
"Primary usage" is clicking a "run" button? Really?
> It could be a lot worse
That is a troubling qualifier, how is inaccessible information better than accessible worthless information?
Making it actually possible to see the deck you officially posted online is neither facilitation or minor.
Thankfully, it is "actually possible" to see the deck.
Here's the touch event handling code: http://code.google.com/p/go/source/browse/present/static/sli...
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.
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.
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++.
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.
Yes, you can still have all goroutines asleep and the program will halt but that's due to improper concurrent design rather than a program implementation mistake.
A program written in Go can deadlock, but the runtime detects deadlock and terminates the program, so at a system level, it does not deadlock.
P.S. "No locks at all, hence no deadlock", is this not true, any system with blocking operations can deadlock. You can construct a Go program that only uses channels and get it to deadlock...
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.
For example, if you launched a task to do some work in the background, and the task deadlocks with one of its own goroutines or even itself (e.g. reads and writes on the same channel), this deadlock will not be reported.
In a large system with many goroutines, I think it is more likely to have a deadlock within a component instead of process-wide, and these will not be reported. (Yet!)
I also locked up a program by inadvertently creating a small goroutine with a busy-loop (the typo being time.Sleep(1)). That little loop prevented any other goroutines from getting scheduled, though it wasn't hard to deduce what was happening, since the CPU was running at 100%.
This may go back to the fact Go is trying to be a systems language, so while it's not C(++) in terms of controlling memory layout and resources, it still has some constructs designed to give more control over the system than something like Erlang or Haskell does (at least in the "default" language subset).
It is possible and indeed easy to do unsafe things in Go. It is not like Erlang or Haskell, in that you must still consider things like what goroutine "owns" a value without any particular language support or enforcement for that decision.