
Why threads vs events is a nonsensical question. - xtacy
http://swtch.com/~rsc/talks/threads07
======
carbonica
Russ is pretty much describing Go as it existed in its developer's minds in
2007. He even explains nearly all of Go's (future) interesting features - with
nearly identical syntax - before he even gets to an example.

~~~
voyou
Well, actually he's describing Alef as it existed in the Plan 9 system in the
early 90s. The creators of Go were also involved in creating Plan 9, so
presumably they drew on their earlier experience of channels in Plan 9 when
they developed the concurrency system for Go.

------
TeMPOraL
A small usability note - I looked at the page, seeing no way to do anything
with it, I reloaded it ("So little text, so much votes on HN - there must be
something there!"), and then - still not knowing what it is - accidentally
clicked on the page, which moved me to the next slide... There is no hint
there that it's a presentation and you can move forward by clicking on it.

~~~
dramaticus3
turn off javascript (I didn't even know it was a slide deck, I have js off
(NotScripts extension) in Chrome).

To be fair to Russ, it is his slidedeck from doing his talk, not a
publication. It was only the 2nd IWP9, we've got a bit better since then :)

~~~
jff
2nd IWP9 was a good one, though--and I got to meet Dennis :)

------
damienkatz
Right, it's never been either/or, they can be used to together. Several years
ago I implemented libevent support at mysql using a pool of threads, allowing
an order of magnitude more client processing. And for many years prior
Microsoft had APIs and examples supporting similar operations on Windows.

------
fauigerzigerk
In my view, the biggest drawback of the evented approach is that logically
sychronous code involving IO becomes very low level spaghetti code. E.g:

    
    
      log(write(process(read(source))))
    

becomes:

    
    
      read(source, function(input) {
        write(process(input), function(success) {
          log(success)
        })
      })

~~~
FooBarWidget
I don't see that as spaghetti code at all, especially when you want to handle
other things while the read() or write() is blocked. Callback-style code
allows you to see the interleaving of events. If you use threads then the
interleaving is non-obvious.

~~~
fauigerzigerk
I guess if you don't see the interleaving in log(write(process(read(source))))
our mental models are too different to merit further discussion of this
aspect.

And of course thread based code can do other things as well during blocking IO
- on other threads.

------
divtxt
The important question is: how can humans correctly write concurrent code?

We think of "threads" as one of the options. The thing is that we've mostly
been using threads with locks (e.g. Java), and slide #3 points out where we
have gone wrong:

    
    
      Drawbacks of threads
        ...
      Drawbacks of events
        ...
      Actually drawbacks of locks and top-level select loops!
    

In fact, it is "locks" that humans have trouble with.

Note the title of the presentation - _Threads without Locks_ \- suggesting
another option. (as others have noted, this presentation describes Go, and I
personally believe this is why Go will do well)

 _edit: wording, formatting_

------
va1en0k
I'm not really sure, but it seems like an Erlang execution model (except for
single-assigment, to which everything can be, and frequently is, automatically
boiled down anyway)

------
hannesw
This is exactly where I want to go with RingoJS: Many threads with private
mutable scope, global read-only scope, worker/actor based thread interop, one
event loop per thread.

Currently we still have shared mutable state that sometimes requires locking
(unless you resort to a functional style of programming):
<http://hns.github.com/2011/05/12/threads.html>

------
lcargill99
Threads tend to be event driven anyway. If one used, say ObjecTime back in the
Olden Days, one could actually configure which FSMs had their own O/S thread
and which were shared on a single thread.

This being said, event-driven as a _design_ choice has much to recommend it.

~~~
jrockway
No, threads tend to be:

    
    
       while( connection = accept_connection ){
           in_thread_run(handle_connection, connection)
       }

~~~
nitrogen
That is event-driven code. A typical accept_connection only returns after
receiving a connection event. The I/O code in handle_connection blocks until
the event that data can be read/written. The event handling just happens in
the OS instead of in userspace with poll/select/etc.

~~~
FooBarWidget
Eh? If you reason that way then _all_ I/O code is evented and that makes the
discussion meaningless. Event-styled code doesn't have anything that blocks
except the event loop I/O multiplexer and everything returns to the event loop
ASAP.

------
nvictor
anyone has the talk or video download?

sometimes you just can't get enough from slides...

~~~
zokier
<http://mirror.cat-v.org/iwp9/2007/videos/Cox.mov>

------
nknight
YES. THANK YOU. I was just ranting about this elsewhere. People go "threads
are evil" with vague rationales about getting locks right and such, and insist
we all use separate heavyweight processes. It's ridiculous.

Selection of sane data structures and communication channels can get you
virtually all of the safety and ease of separate processes WITH the
performance benefits of a shared memory space.

It reminds me of people that criticize C++ for allowing memory leaks. There as
here, simply selecting the right primitives and development strategy in
advance make the problem simply disappear.

~~~
masklinn
> People go "threads are evil" with vague rationales about getting locks right
> and such, and insist we all use separate heavyweight processes.

Insisting processes be used != insisting OS processes be used. Although most
language don't give any choice in the matter.

> Selection of sane data structures and communication channels can get you
> virtually all of the safety and ease of separate processes

It gives you none of the safety, as you have to be very careful in ensuring no
mutable datastructure is ever shared unknowingly. When using processes, you
can't share memory implicitly, which is safe.

> There as here, simply selecting the right primitives and development
> strategy in advance make the problem simply disappear.

That's bullshit. It may make the problem less prominent, but it _can not_ make
the problem disappear.

~~~
marshray
_It may make the problem less prominent, but it can not make the problem
disappear._

It would look quite weird and most other programmers would think you were
crazy for having done it, I think it's possible to write a C++ program that
provably doesn't leak. You could define a custom operator new for every type
which ensures that it gets allocated with some smart pointer or GC heap.

You could probably still use most of the C++ standard library that returns
something needing manual de-allocation (except perhaps new and the old C
malloc itself, which can be banned in various ways).

~~~
nknight
Why would it look quite weird, and why would anyone think you were crazy? It's
standard practice and quite easy in C++. Smart pointers and RAII are your
friends.

~~~
marshray
I agree with you. That's how I code and I don't have any problem with such
leaks. But the usual response by people who don't believe that is "well the
language doesn't force you to use them".

I was thinking of the weird tricks that would have to be in place to plausibly
prove that there was no unmanaged dynamic allocation going on.

