sharing variables between goroutines

4,532 views
Skip to first unread message

germ...@gmx.us

unread,
Jun 12, 2012, 2:18:52 PM6/12/12
to golang-nuts
I'm not experienced in concurrency. I generally assume that if I share
a variable between multiple goroutines that call some method on it
without synchronization bad things can happen.
Are there any exception? Can I share a net.PacketConn between multiple
goroutines and have them just call WriteTo on it whenever they want?
(If I don't care about the order in which packets are sent)
What about having multiple goroutine writing to standard output or a
file? Can something end up in an inconsistent state (besides the data
written)? If no, can you end up with some data from one call to Write
in the middle of data from another call?

What about assignment and evaluation? I read in the FAQ that map
operations are not defined to be atomic, but something else is? Can a
variable be corrupted by parallel assignments? Can it at some point
evaluate to some value that was never meant to be assigned to it?

At the moment I'm interested in the net.PacketConn case but other
situations came to mind. Of course I know I should structure my
programs so that only one goroutine at a time is ever responsible for
a particular piece of data ;)

Thanks,
Leo III

Ian Lance Taylor

unread,
Jun 12, 2012, 3:19:07 PM6/12/12
to germ...@gmx.us, golang-nuts
germ...@gmx.us writes:

> I'm not experienced in concurrency. I generally assume that if I share
> a variable between multiple goroutines that call some method on it
> without synchronization bad things can happen.

That is correct in general.


> Are there any exception? Can I share a net.PacketConn between multiple
> goroutines and have them just call WriteTo on it whenever they want?

Yes, because the net.PacketConn documentation explicitly says "Multiple
goroutines may invoke methods on a PacketConn simultaneously."


> What about having multiple goroutine writing to standard output or a
> file? Can something end up in an inconsistent state (besides the data
> written)? If no, can you end up with some data from one call to Write
> in the middle of data from another call?

The os.File type is a fairly minimal type that just wraps a file
descriptor. So the answers to this more or less depend on your OS. If
you are using GNU/Linux, then the os.File object can not wind up in an
inconsistent state. When os.File is used for a GNU/Linux disk file,
writes go to a specific file position, so they can quite easily be
interleaved, unless you open the file in append mde. Etc.


> What about assignment and evaluation? I read in the FAQ that map
> operations are not defined to be atomic, but something else is? Can a
> variable be corrupted by parallel assignments? Can it at some point
> evaluate to some value that was never meant to be assigned to it?

Local variables can not be corrupted by parallel assignments, as they
are goroutine-specific anyhow. Global variables can be corrupted by
parallel assignments if you don't use appropriate locking.

Ian

germ...@gmx.us

unread,
Jun 12, 2012, 5:20:13 PM6/12/12
to golang-nuts
Thanks for your fast reply.

On Jun 12, 9:19 pm, Ian Lance Taylor <i...@google.com> wrote:
> Yes, because the net.PacketConn documentation explicitly says "Multiple
> goroutines may invoke methods on a PacketConn simultaneously."

D'oh! Sorry for not noticing that.

> > What about assignment and evaluation? I read in the FAQ that map
> > operations are not defined to be atomic, but something else is? Can a
> > variable be corrupted by parallel assignments? Can it at some point
> > evaluate to some value that was never meant to be assigned to it?
>
> Local variables can not be corrupted by parallel assignments, as they
> are goroutine-specific anyhow.  Global variables can be corrupted by
> parallel assignments if you don't use appropriate locking.

My bad for poor choice of words, by "parallel assignment" I was not
meaning something like a, b = c, d
I was thinking about different goroutines assigning to the same
variable (global or shared with a pointer) at the "same time" without
locking, e.g.

func set(p *int, val int) {
*p = val
}

func main() {
a := 0
go set(&a, 1)
go set(&a, 2)
go set(&a, 3)
fmt.Println(a)
}

Can I be sure that this code will always print either 0, 1, 2 or 3 ?
What about other types? If I understand correctly by your reply, the
answer is no.

Thanks,
Leo III

Thomas Bushnell, BSG

unread,
Jun 12, 2012, 5:27:11 PM6/12/12
to Ian Lance Taylor, germ...@gmx.us, golang-nuts
On Tue, Jun 12, 2012 at 12:19 PM, Ian Lance Taylor <ia...@google.com> wrote:
> What about assignment and evaluation? I read in the FAQ that map
> operations are not defined to be atomic, but something else is? Can a
> variable be corrupted by parallel assignments? Can it at some point
> evaluate to some value that was never meant to be assigned to it?

Local variables can not be corrupted by parallel assignments, as they
are goroutine-specific anyhow. 

Is that really true?

func foo() {
var a = 0
func spincrement() {
for {
a++
}
}
go spincrement()
go spincrement()
for {
fmt.Println(a)
}
}

Brad Fitzpatrick

unread,
Jun 12, 2012, 5:48:57 PM6/12/12
to Thomas Bushnell, BSG, Ian Lance Taylor, germ...@gmx.us, golang-nuts
That's an invalid program.  Go makes no promises as to the output.

Thomas Bushnell, BSG

unread,
Jun 12, 2012, 6:29:29 PM6/12/12
to Brad Fitzpatrick, Ian Lance Taylor, germ...@gmx.us, golang-nuts
I know. I'm presenting it as an example of a local variable with is being shared between two goroutines, and is being corrupted by a violation of the memory model's synchronization requirements.

Thomas
 

John Asmuth

unread,
Jun 12, 2012, 6:50:49 PM6/12/12
to golan...@googlegroups.com, Brad Fitzpatrick, Ian Lance Taylor, germ...@gmx.us
He might be commenting on the syntax.

spincrement := func() {
for {
a++
}
}

Ian Lance Taylor

unread,
Jun 12, 2012, 8:13:20 PM6/12/12
to Thomas Bushnell@google.com, BSG, germ...@gmx.us, golang-nuts
You are quite right. I misspoke.

Ian

Ian Lance Taylor

unread,
Jun 12, 2012, 8:14:17 PM6/12/12
to germ...@gmx.us, golang-nuts
germ...@gmx.us writes:

> My bad for poor choice of words, by "parallel assignment" I was not
> meaning something like a, b = c, d
> I was thinking about different goroutines assigning to the same
> variable (global or shared with a pointer) at the "same time" without
> locking, e.g.
>
> func set(p *int, val int) {
> *p = val
> }
>
> func main() {
> a := 0
> go set(&a, 1)
> go set(&a, 2)
> go set(&a, 3)
> fmt.Println(a)
> }
>
> Can I be sure that this code will always print either 0, 1, 2 or 3 ?
> What about other types? If I understand correctly by your reply, the
> answer is no.

Correct: the answer is no.

For details, see http://golang.org/ref/mem .

Ian

cybpycmo

unread,
Jun 12, 2012, 3:51:44 PM6/12/12
to golang-nuts
Just use channels.

I would very surprised if go's net.PacketConn is thread safe, and even
if the default implementation is, we are talking about an interface
here, you cannot rely on that thread safeness for future unknown
implementations of that interface, that some implentations may
implement implicitly on Go.

So, again, to talk to io resources and shared variables, just use
channels:

1) change the direct function call writing on the variable, file or io
for a new function.

2) this function will send the data though a channel.

3) on the other side of the channel there should be a trivial
goroutine composed of a very simple function, this function waits on
the channel and everything that gets there is written to the shared io
or var, and now everything is serialised and atomic.

4) there is only something missing, launching the goroutine passing it
the same channel that the multiple writers will send messages to be
serialised.

You can do something similar for reading and there you might need some
kind of filter to know for which of the potential receivers the
received message is meant.

You should NOT use shared or global variables at all, UNLESS there is
a very good reason for that. And if there is, sometimes a mutex is
simpler than a channel (for bars, not for io most of the time)

And you should not use goroutines just for the sake of it, even though
go ones are really cheap, can have side effects and complicate the
program needlessly. Most of good use good for goroutines are:

- fire and forget, you need to do a task but don't need or want to
wait for it and you don't even need any feedback.

- tell me when done, you need a task to be completed and get some info
from it, but it can be done asynchronously. Here you use a channel
for to send the result back to the initiator.

- subsystems or subapps, or short lived procedures that are
independent, like processing web requests. Or running several
independent apps within a single process.

Jose

Jesse McNelis

unread,
Jun 12, 2012, 11:19:53 PM6/12/12
to cybpycmo, golang-nuts
On Wed, Jun 13, 2012 at 5:51 AM, cybpycmo
<joseluis.v...@gmail.com> wrote:
> I would very surprised if go's net.PacketConn is thread safe, and even
> if the default implementation is, we are talking about an interface
> here, you cannot rely on that thread safeness for future unknown
> implementations of that interface, that some implentations may
> implement implicitly on Go.

It's a requirement of the interface that calls to it are threadsafe.
So if documentation states that it satisfies the net.PacketConn
interface then it's a bug if it's not threadsafe.

Of course before using something as a net.PacketConn you should check
that it actually satisfies the interface, since the compiler can't do
this for you.


--
=====================
http://jessta.id.au
Reply all
Reply to author
Forward
0 new messages