No Thread or Go Routine Locals? What are the alternatives?

1,598 views
Skip to first unread message

Bob Hutchison

unread,
Jun 26, 2012, 1:32:13 PM6/26/12
to golang-nuts
Hi,

As best as I can see there's no such thing as a thread local (which makes sense) nor a Go Routine local (which I'd really like to have). Is that true?

What do people do instead? You can pass stuff around, but is there a better way?

Thanks,
Bob

John Asmuth

unread,
Jun 26, 2012, 1:39:07 PM6/26/12
to golan...@googlegroups.com
You need to do this on your own. As you said, passing stuff around is the only approach viable here.

If you feel the need to have goroutine-local data, maybe try to figure out a way to avoid that entirely. These kinds of requirements make for confusing code-bases.

Perhaps if you tell us the problem you're trying to solve, we can suggest an alternate route?

- John

Ian Lance Taylor

unread,
Jun 26, 2012, 1:45:30 PM6/26/12
to Bob Hutchison, golang-nuts
Bob Hutchison <hutch-...@recursive.ca> writes:

> As best as I can see there's no such thing as a thread local (which
> makes sense) nor a Go Routine local (which I'd really like to
> have). Is that true?

That is true. The language encourages the casual use of goroutines.
That makes goroutine-local data much less useful, since it would not be
available as soon as you add a go statement.

Ian

Bob Hutchison

unread,
Jun 26, 2012, 4:25:41 PM6/26/12
to John Asmuth, golan...@googlegroups.com
On 2012-06-26, at 1:39 PM, John Asmuth wrote:

You need to do this on your own. As you said, passing stuff around is the only approach viable here.

If you feel the need to have goroutine-local data, maybe try to figure out a way to avoid that entirely. These kinds of requirements make for confusing code-bases.

I agree in general, but occasionally it would be the cleanest solution.


Perhaps if you tell us the problem you're trying to solve, we can suggest an alternate route?

Thanks, but at the moment it's mostly a theoretical thing. I'm working on something similar to some older Java and Ruby projects, and the Java solution used thread locals for among other things holding locks.

Cheers,
Bob

Bob Hutchison

unread,
Jun 26, 2012, 4:27:06 PM6/26/12
to Ian Lance Taylor, golang-nuts
I suppose it's too much to ask for Common Lisp like dynamic variables that work across go statements :-) I wouldn't need the go-local variables then.

Cheers,
Bob

>
> Ian

Rémy Oudompheng

unread,
Jun 26, 2012, 4:53:56 PM6/26/12
to Bob Hutchison, Ian Lance Taylor, golang-nuts
On 2012/6/26 Bob Hutchison <hutch-...@recursive.ca> wrote:
> I suppose it's too much to ask for Common Lisp like dynamic variables that work across go statements :-) I wouldn't need the go-local variables then.

I'm not use what you mean here. It is common to use closures in go
statements and then you can capture an outer variable:

contexts := make([]Context, N)
for i := range contexts {
go contexts[i].DoSomething()
}

or simply:

go func () {
ctx := NewContext()
ctx.DoSomething()
}()

and then put all your local variables in the Context. In the general
case, I am already pretty happy with variables local to functions.

Rémy.

Bob Hutchison

unread,
Jun 27, 2012, 9:05:55 AM6/27/12
to Rémy Oudompheng, Ian Lance Taylor, golang-nuts
These are good ideas. The trouble starts when you have to call functions from the body of DoSomething(). The context has to be carried as an argument to these functions (either as a parameter or the dispatching object). If you had goroutine locals you could arrange for ctx to be referred to in one.

I also wonder about the use of a method dispatch on the context as you use in your examples. If you look at the thread "A Rubyist has Some Difficulties With Go" (you commented there) you'll see a discussion about method calls (and possible loss of runtime type information) vs. function calls with interfaces. Do you have any thoughts on that issue in this discussion?

Cheers,
Bob

>
> Rémy.

Øyvind Teig

unread,
Jun 27, 2012, 10:41:33 AM6/27/12
to golan...@googlegroups.com, John Asmuth


kl. 22:25:41 UTC+2 tirsdag 26. juni 2012 skrev hutch følgende:

On 2012-06-26, at 1:39 PM, John Asmuth wrote:

You need to do this on your own. As you said, passing stuff around is the only approach viable here.

If you feel the need to have goroutine-local data, maybe try to figure out a way to avoid that entirely. These kinds of requirements make for confusing code-bases.

I agree in general, but occasionally it would be the cleanest solution.


Perhaps if you tell us the problem you're trying to solve, we can suggest an alternate route?

Thanks, but at the moment it's mostly a theoretical thing. I'm working on something similar to some older Java and Ruby projects, and the Java solution used thread locals for among other things holding locks.

I would want to rethink locks. If you need to protect a common resource then pack handling of that resource inside a goroutine that always "runs", and send operations on that resource over channels to the goroutine. Don't think about accessing that shared resource in any other goroutine, it will break protection. Then you don't need to be afraid of p and v on the semaphore locks, not priority inversion (there), no forgotten v or v erroneously first and no nesting problems. If go uses semaphores to implement channels they could (but do they need that when there is no preemption?), it's for us to forget about them.

Are there cases when semaphore locks are needed any more (within this paradigm)?

- Øyvind

John Asmuth

unread,
Jun 27, 2012, 11:52:24 AM6/27/12
to golan...@googlegroups.com, John Asmuth
On Wednesday, June 27, 2012 10:41:33 AM UTC-4, Øyvind Teig wrote:
Are there cases when semaphore locks are needed any more (within this paradigm)?

The times I find that a lock is better than a goroutine/channel solution are things where you need an immediate response. For instance, if you want to have a shared map. Setting up the channel infrastructure to send the value back, and make sure the caller maps it to the appropriate key, is a waste of time (unless it fits into some larger picture). Much easier to just lock map accesses.

I'm happy to be shown why I'm wrong here, and what an effective and simple channel/goroutine-based solution might look like. 

Jim Whitehead II

unread,
Jun 28, 2012, 3:35:39 AM6/28/12
to Øyvind Teig, golan...@googlegroups.com, John Asmuth
On Wed, Jun 27, 2012 at 4:52 PM, John Asmuth <jas...@gmail.com> wrote:
> On Wednesday, June 27, 2012 10:41:33 AM UTC-4, Øyvind Teig wrote:
>>
>> Are there cases when semaphore locks are needed any more (within this
>> paradigm)?
>
>
> The times I find that a lock is better than a goroutine/channel solution are
> things where you need an immediate response. For instance, if you want to
> have a shared map. Setting up the channel infrastructure to send the value
> back, and make sure the caller maps it to the appropriate key, is a waste of
> time (unless it fits into some larger picture). Much easier to just lock map
> accesses.
>
> I'm happy to be shown why I'm wrong here, and what an effective and simple
> channel/goroutine-based solution might look like.

Since we know from both theory and practice that semaphore locking
mechanisms and a channel infrastructure (either buffered with an
implicit ownership transfer or an explicit server to manage the
resource) are semantically equivalent, it comes down to a matter of
optimization. Sure, you can write this using only channels and in the
right process network that makes absolute sense. However, if you're
forcing a particular implementation style and need something, like as
John suggests a shared map, then locks may be the right thing to use.

We still inline procedure calls. Replacing one locking mechanism with
another that is semantically equivalent and more efficient doesn't
need to break the abstraction of the system, and it can provide a
performance benefit.

- Jim

Jim Whitehead II

unread,
Jun 28, 2012, 3:41:31 AM6/28/12
to John Asmuth, golan...@googlegroups.com
On Wed, Jun 27, 2012 at 4:52 PM, John Asmuth <jas...@gmail.com> wrote:
You could replace the mutex with a channel with a buffer of 1 and use
it to transfer ownership of the resource. The pattern here is
identical to locking and unlocking (receiving and sending) and there's
no additional goroutine of overhead. Of course, you'd just be
emulating an explicit lock with a channel that is itself implemented
with explicit locks. I think you're absolutely right.. channels are a
powerful model for thinking about concurrent software, but they will
not make sense in every program for the same reason that procedure
calls are still in-lined in many languages.

- Jim

Øyvind Teig

unread,
Jun 28, 2012, 7:08:03 AM6/28/12
to golan...@googlegroups.com, John Asmuth
kl. 09:41:31 UTC+2 torsdag 28. juni 2012 skrev Jim Whitehead følgende:
On Wed, Jun 27, 2012 at 4:52 PM, John Asmuth wrote:
> On Wednesday, June 27, 2012 10:41:33 AM UTC-4, Øyvind Teig wrote:
>>
>> Are there cases when semaphore locks are needed any more (within this
>> paradigm)?
>
>
> The times I find that a lock is better than a goroutine/channel solution are
> things where you need an immediate response. For instance, if you want to
> have a shared map. Setting up the channel infrastructure to send the value
> back, and make sure the caller maps it to the appropriate key, is a waste of
> time (unless it fits into some larger picture). Much easier to just lock map
> accesses.
>
> I'm happy to be shown why I'm wrong here, and what an effective and simple
> channel/goroutine-based solution might look like.

You could replace the mutex with a channel with a buffer of 1 and use
it to transfer ownership of the resource. The pattern here is
identical to locking and unlocking (receiving and sending) and there's
no additional goroutine of overhead. Of course, you'd just be
emulating an explicit lock with a channel that is itself implemented
with explicit locks.

Really? Some at golang-nuts said there was no preemption, and scheduling is done at (channel) synchronization points and at "some system calls", so was still necessary to for Google to use locks to implement the channel primitive? Where is the code for this? I have tried to make a clone of the code, but I must have misunderstood something. There are no semaphores in many CSP-based systems if there are light weight processes and no map between a process and another operating system process. (But CSPlib on top of Posix uses several mutexes and semaphores per channel). But Go, will it need it?
 
I think you're absolutely right.. channels are a
powerful model for thinking about concurrent software, but they will
not make sense in every program for the same reason that procedure
calls are still in-lined in many languages.
 
This would as much as reflecting on how channels are nice or not nice, reflect on what is the process model? What are goroutines used for? Encapsulation only or concurrency, or both? When something has persistent state that needs to survive a call, I would rather use a process than an object, if that process later on would need to supply some other concurrent user a service. Then I would use objects inside a process. So, referring to the Map comment above, why be so afraid of memcpy? 

There are CSP-papers on zero-copying over channels as well. You just pass over ownership. But then the object sent over that channel needs to be non-existing and out of scope for the sender when it's sent. But then the channel needs to know if it does need to do copying anyhow, if it's across a processor. But this is Go, and it's not like that. 

But as for some of the comments I read here on golang-nuts, the community seems to have some way yo go to accept a goroutine / channel as a first class citizen of the language. Are the concepts in Go really so expensive that true Process-Oriented Progarrming is not possible (http://en.wikipedia.org/wiki/Process-oriented_programming)? 

- Øyvind

- Jim

Rémy Oudompheng

unread,
Jun 28, 2012, 7:22:55 AM6/28/12
to Bob Hutchison, Ian Lance Taylor, golang-nuts
On 2012/6/27 Bob Hutchison <hutch-...@recursive.ca> wrote:
> These are good ideas. The trouble starts when you have to call functions from the body of DoSomething(). The context has to be carried as an argument to these functions (either as a parameter or the dispatching object). If you had goroutine locals you could arrange for ctx to be referred to in one.
>

There are already ways to capture variables in function definitions
without having to pass them as arguments. You can pretty much write:

ctx := NewContext()
DoSomething := func() { Frobnicate(ctx.Foo) }
DoOtherthing := func() { Foobarize(ctx.Bar) }
go func() {
DoSomething()
DoOtherthing()
}()

> I also wonder about the use of a method dispatch on the context as you use in your examples. If you look at the thread "A Rubyist has Some Difficulties With Go" (you commented there) you'll see a discussion about method calls (and possible loss of runtime type information) vs. function calls with interfaces. Do you have any thoughts on that issue in this discussion?
>

There is no loss of type information in a method call.

Rémy.

Bob Hutchison

unread,
Jun 28, 2012, 8:58:45 AM6/28/12
to Rémy Oudompheng, Ian Lance Taylor, golang-nuts

On 2012-06-28, at 7:22 AM, Rémy Oudompheng wrote:

> On 2012/6/27 Bob Hutchison <hutch-...@recursive.ca> wrote:
>> These are good ideas. The trouble starts when you have to call functions from the body of DoSomething(). The context has to be carried as an argument to these functions (either as a parameter or the dispatching object). If you had goroutine locals you could arrange for ctx to be referred to in one.
>>
>
> There are already ways to capture variables in function definitions
> without having to pass them as arguments. You can pretty much write:
>
> ctx := NewContext()
> DoSomething := func() { Frobnicate(ctx.Foo) }
> DoOtherthing := func() { Foobarize(ctx.Bar) }
> go func() {
> DoSomething()
> DoOtherthing()
> }()

Hmm, interesting idea... I've got to keep it in mind. I think I'd have to do this as some kind of closure... I don't know if it's practical for me though... In a smallish Ruby project that I've just checked, at this moment there are (at least) 1451 functions that would need to be 'in' each closure. And I'd have possibly a few hundred contexts, though only a few would be 'active' at a time. I've considered the possibility of having one executable for each context (for both the Ruby and a possible Go version)

>
>> I also wonder about the use of a method dispatch on the context as you use in your examples. If you look at the thread "A Rubyist has Some Difficulties With Go" (you commented there) you'll see a discussion about method calls (and possible loss of runtime type information) vs. function calls with interfaces. Do you have any thoughts on that issue in this discussion?
>>
>
> There is no loss of type information in a method call.

From a Go point of view no, from an OO point of view definitely. I have reconciled myself to this as described in the followup (http://t.co/F5MDTjfy) to the post that's being discussed in that thread.

>
> Rémy.

Ian Lance Taylor

unread,
Jun 28, 2012, 2:04:59 PM6/28/12
to Øyvind Teig, golan...@googlegroups.com
Øyvind Teig <oyvin...@teigfam.net> writes:

> Really? Some at golang-nuts said there was no preemption, and scheduling is
> done at (channel) synchronization points and at "some system calls", so was
> still necessary to for Google to use locks to implement the channel
> primitive?

There is no preemption, and scheduling is done at synchronization
points, but Go programs run multi-threaded. Locks are required.

> Where is the code for this?

src/pkg/runtime

notably chan.c and proc.c.

Ian

Øyvind Teig

unread,
Jun 28, 2012, 2:42:47 PM6/28/12
to golan...@googlegroups.com, Øyvind Teig
kl. 20:04:59 UTC+2 torsdag 28. juni 2012 skrev Ian Lance Taylor følgende:
Øyvind Teig writes:

> Really? Some at golang-nuts said there was no preemption, and scheduling is
> done at (channel) synchronization points and at "some system calls", so was
> still necessary to for Google to use locks to implement the channel
> primitive?

There is no preemption, and scheduling is done at synchronization
points, but Go programs run multi-threaded.  Locks are required.

If all communication is over channels, and channels also drive the synchronization, in addition to some system calls you would only "require" locks if you need to keep ownership of a shared object between synchronization points. This is probably a too stringent a requirement, because then the programmer would need to relate to synchronization points.

If there were no shared objects other than those packed into a goroutine with access messages over channels, then one would not need locks. But I assume not having shared objects would again be too stringent, and the language would not be as flexible as needed to expand the user base, on all sides of the big dips. Occam learned that lesson (and now only exists as a research platform), but the new and nice XC by XMOS is [1] has done the same: no shared anything. And it has usage rules that are verified by the compiler. Actually it may share (like a screen buffer), but not the same pixels (99.9% certain on that one, but occam guaranteed this). 

So, the designers of Go, with the model they have used, did not _need_ to allow shared objects of that sort and locks. To the best of my reasoning it's a design choice that is not related to Go programs being multithreaded.

This thread of arguments may be falsified if Go programs have to use Linux, Windows, Mac OS semaphores for system-wide shared resources. Is this the real reason for Go locks (or is it the rather pragmatic language design)?


> Where is the code for this?

src/pkg/runtime

notably chan.c and proc.c.

Thanks!

- Øyvind 

Ian

Jim Whitehead II

unread,
Jun 28, 2012, 3:04:09 PM6/28/12
to Øyvind Teig, golan...@googlegroups.com, John Asmuth
I'm not extolling the virtues of goroutines, I'm simply talking about
one use. The question here was about about a thread-safe map data
structure. You have several options for making that available in a
thread-safe way. One is using an explicit locking mechanism using
either a mutex, semaphore, or even a buffered channel. Another is a
goroutine that owns the map and exports some interface (using channels
or explicit interfaces) that can be used to access and alter that data
structure.

> There are CSP-papers on zero-copying over channels as well. You just pass
> over ownership. But then the object sent over that channel needs to be
> non-existing and out of scope for the sender when it's sent. But then the
> channel needs to know if it does need to do copying anyhow, if it's across a
> processor. But this is Go, and it's not like that.
>
> But as for some of the comments I read here on golang-nuts, the community
> seems to have some way yo go to accept a goroutine / channel as a first
> class citizen of the language. Are the concepts in Go really so expensive
> that true Process-Oriented Progarrming is not possible
> (http://en.wikipedia.org/wiki/Process-oriented_programming)?

Of course not, I'm not even remotely making that claim. My thesis in
fact argues that the CSP-style concurrent method of software
engineering that is enabled by the Go programming language is a very
powerful paradigm.

I was discussing a very concrete example: that of a thread-safe shared
map data structure. I even said that I would avoid creating such a
thing in my systems, but any solution in a system that uses one will
require mutual exclusion without a question.

On commodity hardware this is typically done using atomic instructions
and some form of language/kernel provided lock structure. On the XMOS
architecture, I believe these could probably be supported natively
without that overhead.

The fact is, today, if you're writing a Go program and need to provide
access to a map data structure in a thread-safe way, there are a few
options you can choose. Structurally they are different, but the
common factor is runtime.futex, which provides mutual exclusion.

- Jim

Øyvind Teig

unread,
Jun 28, 2012, 3:30:10 PM6/28/12
to golan...@googlegroups.com, Øyvind Teig, John Asmuth
kl. 21:04:09 UTC+2 torsdag 28. juni 2012 skrev Jim Whitehead følgende:
I do find these arguments (and so may other arguments in goland-nuts) as very insightful! Thanks! I note what you are saying, and the idea that Go's concurrency model is based upon (CSP) is so solid that it takes beeing somewhat hit with new or other thoughts and interpretations! What I hope for the most is that Go will become "the next" C or C++, and then CSP comes with it. Some have learned CSP over the years and never come upon anything more solid. Now, keep up with educating the OO-only thinkers and the stringent CSP-thinkers!-)

- Øyvind


Ian Lance Taylor

unread,
Jun 28, 2012, 3:35:28 PM6/28/12
to Øyvind Teig, golan...@googlegroups.com
Øyvind Teig <oyvin...@teigfam.net> writes:

> kl. 20:04:59 UTC+2 torsdag 28. juni 2012 skrev Ian Lance Taylor følgende:
>>
>> Øyvind Teig writes:
>>
>> > Really? Some at golang-nuts said there was no preemption, and scheduling
>> is
>> > done at (channel) synchronization points and at "some system calls", so
>> was
>> > still necessary to for Google to use locks to implement the channel
>> > primitive?
>>
>> There is no preemption, and scheduling is done at synchronization
>> points, but Go programs run multi-threaded. Locks are required.
>>
>
> If all communication is over channels, and channels also drive the
> synchronization, in addition to some system calls you would only "require"
> locks if you need to keep ownership of a shared object between
> synchronization points. This is probably a too stringent a requirement,
> because then the programmer would need to relate to synchronization points.

I'm sorry, I don't understand what you are talking about. I was talking
about the runtime implementation of the channel send and receive
operations in Go. Those require locks. That will be true for any
multi-threaded implementation of channels, because the channels
themselves are a shared object.

Ian

si guy

unread,
Jun 28, 2012, 9:44:36 PM6/28/12
to golan...@googlegroups.com
In my limited experience with go channel based approaches are much less bug prone than lock based approaches and seem to be easier to conceptualize than locks, they also seem to be easier to profile with printlns and such and they assist with modularity(at least for me). You can treat the context (or whatever) as a physical resource that only exists in one place at a time(re-enforced by channel access idioms that are never deviated from). In either case mixing the two I find can be confusing.

In reference to those idioms, I liken one such pattern to volatiles in c#(which btw is a messy affair in c#), where a public method in go is implemented as a channel send to the "center" of a struct which has a state machine (or run loop)at its heart, and as a result is totally safe. And when a response is needed the request{chan response} idiom can hide behind the method interface. All very neatly.

Simon Watt

Øyvind Teig

unread,
Jun 29, 2012, 2:08:51 AM6/29/12
to golan...@googlegroups.com, Øyvind Teig
I was trying to explain why, for certain systems, what you say is not valid. 

Channels are shared objects, yes, but in some CSP-based systems with scheduling on synchronization points they do not need to be locked. I have drawn some arguments above (some more below). Ref. occam on transputers (micro-coded CSP), SPoC occam-to-C translator which delivered very nice C-code, a couple of C-based libraries we have built here at work (papers about this at http://www.teigfam.net/oyvind/pub/pub.html), and most other I have seen papers about (at WoTUG/CPA conferences over the last 20 years).

I am just trying to understand why the Go designers obviously had to use locks to build channels, more like the mentioned CSPLib on top of Linux. Especially based on the fact that scheduling is non-preemptive and based on synchronization points. 

This is not the same discussion as the need for locks altogether, even if that is also part of this thread. It's about the need for locks to implement channels in the go runtime system. Obsviously it's there, but just stating that they are "shared" or "multi-threaded" is really not enough. There seems to be more subtle reasons?

I'll add some more points. For a channel, both sender and receiver are blocked while the memcpy happens. The second part isn't in the formal sense, but related to the channel it is. (If buffered, consider the buffer position as a "process"). And since one process is not able to be descheduled anywhere (no preemption), only one side will at any time see the shared channel. It can inspect the "first" variable to see if it was first "thread-safely", and also write to it if it needs to. If it was first, it will be descheduled (if it was in a select all the other components also need to be first in that case, if not the select is taken). So, the other part of it (even if many-to-one or one-to-many, I believe) need not be afraid that there is a first there smelling on the channel object. The need for locks in this regime is not present.

At a CPA conference I remember there was a very nice lecture by Peter Welch (University of Kent at Canterbury, UK) about channels, I think it was for occam-π. Logically a channel is a "permission to do an operation" I think is how he said it.

But obviously I am missing something here, the regime I use as a precondition to my arguments doesn't seem to be the Go regime. I may have "too much" original occam background. What's the crux?

- Øyvind

si guy

unread,
Jun 29, 2012, 2:54:59 AM6/29/12
to golan...@googlegroups.com
I think this has something to do with the first class nature of channels in go. If I remember correctly they are not in occam, in fact I don't think you can have multiple readers or writers in parallel to a channel.

Øyvind Teig

unread,
Jun 29, 2012, 7:46:40 AM6/29/12
to golan...@googlegroups.com
kl. 08:54:59 UTC+2 fredag 29. juni 2012 skrev si guy følgende:
I think this has something to do with the first class nature of channels in go. If I remember correctly they are not in occam, in fact I don't think you can have multiple readers or writers in parallel to a channel.
  • The traditionalists would say that occam's channels are indeed first class nature. Many-to-one is done with an array of point-to-point channels and ALT on the reader side, while one-to-many is also with an array of point-to-point channels with perhaps PAR sending.
  • The sceptics may say that Go's channel is not first class nature, since there is no usage chacks on them by the compiler. And there are no input conditions (a receiver cannot hold a group of clients if there is one session going on, it must be simulated by sender over new channels to do the session on). See comment on golang-nuts lately.
  • But I seem to like Go's channels as well, they seem both "richer" and a "poorer"!
But whether this has to do with channels having to be implemented with locks, is still an open question. Only one goroutine may smell on a channel per "system tick" (in the Promela sense), because of the scheduling. Etc..

- Øyvind

Julian Phillips

unread,
Jun 29, 2012, 8:11:17 AM6/29/12
to Øyvind Teig, golan...@googlegroups.com
I think the part you are missing is that goroutines are scheduled onto
OS threads, and that more than one thread can be running a goroutine at
once. So the runtime does have to deal with pre-emptive threads and
parallel execution.

--
Julian

Henrik Johansson

unread,
Jun 29, 2012, 8:17:38 AM6/29/12
to Julian Phillips, golan...@googlegroups.com

Can more than one thread really run the same goroutine concurrently?

It seems weird.

/Henrik

chris dollin

unread,
Jun 29, 2012, 8:31:26 AM6/29/12
to Henrik Johansson, Julian Phillips, golan...@googlegroups.com
On 29 June 2012 13:17, Henrik Johansson <dahan...@gmail.com> wrote:
> Can more than one thread really run the same goroutine concurrently?
>
> It seems weird.

>> I think the part you are missing is that goroutines are scheduled onto OS
>> threads, and that more than one thread can be running a goroutine at once.

Not the /same/ "a goroutine". Different threads may be running, so different
goroutines may be running at the same time.

Chris

--
Chris "allusive" Dollin

Øyvind Teig

unread,
Jun 29, 2012, 9:04:14 AM6/29/12
to golan...@googlegroups.com, Henrik Johansson, Julian Phillips


kl. 14:31:26 UTC+2 fredag 29. juni 2012 skrev chris dollin følgende:
On 29 June 2012 13:17, Henrik Johansson wrote:
> Can more than one thread really run the same goroutine concurrently?
>
> It seems weird.

>> I think the part you are missing is that goroutines are scheduled onto OS
>> threads, and that more than one thread can be running a goroutine at once.

Not the /same/ "a goroutine". Different threads may be running, so different
goroutines may be running at the same time.

Chris

--
Chris "allusive" Dollin

One goroutine == one Windows/Linux/OSX thread or process? Would be a rather huge overhead in context switching, cache misses and default memory per process? Is this really so?

If so, different goroutines may possibly run at the same time. But since the goroutine process model and scheduling deschedules at synchronization points will they actually run at the same time - while treating a common channel? The go runtime system would perhaps do the "run-type" scheduling?

Which thread does the go runtime system run in? Its own or underneath all gorotines? 

Where is this described to the level that I (perhaps we) need to fully understand?

- Øyvind


 

Henrik Johansson

unread,
Jun 29, 2012, 9:17:45 AM6/29/12
to Øyvind Teig, golan...@googlegroups.com, Julian Phillips

No, several goroutines are multiplexed onto a set of threads.

/Henrik

Øyvind Teig

unread,
Jun 29, 2012, 9:25:10 AM6/29/12
to golan...@googlegroups.com, Øyvind Teig, Julian Phillips
On a one-to-one basis or grouped?
What is the semantics of multiplexing here?

Another thing regarding the need for locks for low level implementation of go channels. A semaphore lock will be waited for if it's not avaiable. When do you need to "wait for" a channel? The channels are usually able to schedule the processes directly, with the "first" and "second" on the channel scheme. A critical region will just keep preempted/interrupt code out of the way. More help needed!  


kl. 15:17:45 UTC+2 fredag 29. juni 2012 skrev Henrik Johansson følgende:

No, several goroutines are multiplexed onto a set of threads.

/Henrik

Julian Phillips

unread,
Jun 29, 2012, 9:25:51 AM6/29/12
to Øyvind Teig, golan...@googlegroups.com, Henrik Johansson
On Fri, 29 Jun 2012 06:04:14 -0700 (PDT), Øyvind Teig wrote:
> kl. 14:31:26 UTC+2 fredag 29. juni 2012 skrev chris dollin følgende:
>>
>> On 29 June 2012 13:17, Henrik Johansson wrote:
>> > Can more than one thread really run the same goroutine
>> concurrently?
>> >
>> > It seems weird.
>>
>> >> I think the part you are missing is that goroutines are scheduled
>> onto
>> OS
>> >> threads, and that more than one thread can be running a goroutine
>> at
>> once.
>>
>> Not the /same/ "a goroutine". Different threads may be running, so
>> different
>> goroutines may be running at the same time.
>>
>> Chris
>>
>> --
>> Chris "allusive" Dollin
>>
>
> One goroutine == one Windows/Linux/OSX thread or process? Would be a
> rather
> huge overhead in context switching, cache misses and default memory
> per
> process? Is this *really* so?

No, there is an N:1 mapping between goroutines and threads.

> If so, different goroutines may *possibly* run at the same time. But
> since
> the goroutine process model and scheduling deschedules at
> synchronization
> points will they *actually* run at the same time - while treating a
> common
> channel? The go runtime system would perhaps do the "run-type"
> scheduling?

The Go runtime multiplexes goroutines onto a set of OS threads, up to
GOMAXPROCS threads may be running goroutines at the same time to provide
parallelism in addition to concurrency. So multiple goroutines may be
running on different threads and accessing the same channel at the same
time, and the Go scheduler won't be involved.

In addition to the threads running goroutines there may be any number
of threads waiting on system calls or running C code via cgo, they don't
count towards GOMAXPROCS.

> Which thread does the go runtime system run in? Its own or underneath
> all
> gorotines?

The runtime runs in the same thread that the goroutine was running in
before it hit a synch-point.

> Where is this described to the level that I (perhaps we) need to
> fully
> understand?

I think most of my understanding comes from a combination of reading
the code and this list ... there may be a blog port or wiki page with a
decent overview, but I'm afraid I don't know of it.

> - Øyvind

--
Julian

chris dollin

unread,
Jun 29, 2012, 9:28:20 AM6/29/12
to Øyvind Teig, golan...@googlegroups.com, Henrik Johansson, Julian Phillips
On 29 June 2012 14:04, Øyvind Teig <oyvin...@teigfam.net> wrote:
>
>
> kl. 14:31:26 UTC+2 fredag 29. juni 2012 skrev chris dollin følgende:
>>
>> On 29 June 2012 13:17, Henrik Johansson wrote:
>> > Can more than one thread really run the same goroutine concurrently?
>> >
>> > It seems weird.
>>
>> >> I think the part you are missing is that goroutines are scheduled onto
>> >> OS
>> >> threads, and that more than one thread can be running a goroutine at
>> >> once.
>>
>> Not the /same/ "a goroutine". Different threads may be running, so
>> different
>> goroutines may be running at the same time.

> One goroutine == one Windows/Linux/OSX thread or process?

That depends what you mean by that.

> Would be a rather
> huge overhead in context switching, cache misses and default memory per
> process? Is this really so?

A goroutine switch isn't a thread switch. Any given running goroutine is
running on /some/ thread. When it suspends, that thread can run a
different goroutine. A thread switch doesn't happen (there).

> If so, different goroutines may possibly run at the same time.

Precisely.

Devon H. O'Dell

unread,
Jun 29, 2012, 9:31:02 AM6/29/12
to Øyvind Teig, golan...@googlegroups.com, Henrik Johansson, Julian Phillips
2012/6/29 Øyvind Teig <oyvin...@teigfam.net>:
>
>
> kl. 14:31:26 UTC+2 fredag 29. juni 2012 skrev chris dollin følgende:
>>
>> On 29 June 2012 13:17, Henrik Johansson wrote:
>> > Can more than one thread really run the same goroutine concurrently?
>> >
>> > It seems weird.
>>
>> >> I think the part you are missing is that goroutines are scheduled onto
>> >> OS
>> >> threads, and that more than one thread can be running a goroutine at
>> >> once.
>>
>> Not the /same/ "a goroutine". Different threads may be running, so
>> different
>> goroutines may be running at the same time.
>>
>> Chris
>>
>> --
>> Chris "allusive" Dollin
>
>
> One goroutine == one Windows/Linux/OSX thread or process? Would be a rather
> huge overhead in context switching, cache misses and default memory per
> process? Is this really so?

No. N goroutines are multiplexed over M OS threads. M is defined by
the value of GOMAXPROCS, which someone may also set in init() if they
do not want to rely on an environment variable being supplied. Your
observations about cache coherency and context switching are valid;
these things haven't been a big enough problem (for anyone yet) to
fix.

> If so, different goroutines may possibly run at the same time. But since the
> goroutine process model and scheduling deschedules at synchronization points
> will they actually run at the same time - while treating a common channel?
> The go runtime system would perhaps do the "run-type" scheduling?

If your data model shares data between multiple goroutines that
perform some analysis on the data, and that analysis does not cause
the goroutine to yeild, these goroutines can and will run at the same
time. Reading from or writing to a channel causes the goroutine to
yield.

> Which thread does the go runtime system run in? Its own or underneath all
> gorotines?
>
> Where is this described to the level that I (perhaps we) need to fully
> understand?

Ian linked previously to both src/pkg/runtime/proc.c and
src/pkg/runtime/chan.c; that's the definitive location for this
information.

> - Øyvind

--dho

>
>

Ian Lance Taylor

unread,
Jun 29, 2012, 3:21:19 PM6/29/12
to Øyvind Teig, golan...@googlegroups.com
On Thu, Jun 28, 2012 at 11:08 PM, Øyvind Teig <oyvin...@teigfam.net> wrote:
> kl. 21:35:28 UTC+2 torsdag 28. juni 2012 skrev Ian Lance Taylor følgende:
>
> I was trying to explain why, for certain systems, what you say is not
> valid.

OK. I'm only talking about Go. I'm not familiar with the runtime
implementation of other systems.

> This is not the same discussion as the need for locks altogether, even if
> that is also part of this thread. It's about the need for locks to implement
> channels in the go runtime system. Obsviously it's there, but just stating
> that they are "shared" or "multi-threaded" is really not enough. There seems
> to be more subtle reasons?

I don't think so.

> I'll add some more points. For a channel, both sender and receiver are
> blocked while the memcpy happens. The second part isn't in the formal sense,
> but related to the channel it is. (If buffered, consider the buffer position
> as a "process"). And since one process is not able to be descheduled
> anywhere (no preemption), only one side will at any time see the shared
> channel.

No. Go programs are multi-threaded. Multiple goroutines can run
simultaneously.

Go programs are (currently) non-preemptive but they are still multi-threaded.

Ian

Ian Lance Taylor

unread,
Jun 29, 2012, 3:26:57 PM6/29/12
to Øyvind Teig, golan...@googlegroups.com, Julian Phillips
On Fri, Jun 29, 2012 at 6:25 AM, Øyvind Teig <oyvin...@teigfam.net> wrote:
>
> Another thing regarding the need for locks for low level implementation of
> go channels. A semaphore lock will be waited for if it's not avaiable. When
> do you need to "wait for" a channel? The channels are usually able to
> schedule the processes directly, with the "first" and "second" on the
> channel scheme. A critical region will just keep preempted/interrupt code
> out of the way. More help needed!

Again, channels are shared objects, and Go programs are
multi-threaded. It is possible for two different goroutines, running
on different threads to simultaneously attempt to send a value on a
channel. They will both attempt to lock the channel simultaneously.
The goroutine that acquires the lock will add data to the channel,
then release the lock. Then the second goroutine will acquire the
lock, and (assuming a buffered channel with enough space), add its
data.

Yes, you can think of this as a critical region if you like. A
critical region is just another kind of lock.

Ian
Message has been deleted

Øyvind Teig

unread,
Jul 5, 2012, 6:40:05 AM7/5/12
to golan...@googlegroups.com, Øyvind Teig
Found the source code documentation of channels and processes at:
- Øyvind 

Thanks!

- Øyvind 

Ian
Reply all
Reply to author
Forward
0 new messages