Updating the shared variable with goroutines

2,272 views
Skip to first unread message

Andrew Z

unread,
Jul 18, 2012, 12:19:23 PM7/18/12
to golang-nuts
Hello,
 what is the proper way to update ae shared variable(s) by goroutines?

Here is the sample code http://play.golang.org/p/2wOUqsOyst ( won't work due to sleep)
i think this (http://play.golang.org/p/CB_kw8Bvjc) version is slightly closer to the truth, but i think the logic is wrong, plus it throws
"throw: all goroutines are asleep - deadlock!" exception

Please advise.
AZ

Andrew

unread,
Jul 18, 2012, 12:33:15 PM7/18/12
to golan...@googlegroups.com
I think this (http://play.golang.org/p/Uc9vlblxMA) version is step close logic wise.
But still puzzled with the deadlocking.

Paul Borman

unread,
Jul 18, 2012, 12:57:20 PM7/18/12
to Andrew, golan...@googlegroups.com
If it is an int type look at the sync/atomic package.  If it is more complicated then you can use a sync.Mutex.

func (ms *MyStruct) IncCounter() int32 {
    return atomic.AddInt32(&ms.counter, 1)
}

func (ms *MyStruct) IncCounter() int {
    ms.mu.Lock()
    defer ms.mu.Unlock()
    ms.counter++
    return ms.counter
}

or, as in your direction, fire off a goroutine that reads a channel stored with ms.  If you need the value it incremented to then you can make a channel that takes something like:

type Request type {
    Delta MyType
    C chan MyType
}

and end up doing:

r := Request{Delta: value; make(chan MyType)}
ms.ch <- r
newValue := <- r.C

Hope this gives you some ideas

    -Paul

PS:  All the code above was typed into the mail not actually run.

Andrew Z

unread,
Jul 18, 2012, 4:33:17 PM7/18/12
to Paul Borman, golan...@googlegroups.com
Paul,
 thank you for the ideas. It helped.
I ended up with semi working version (http://play.golang.org/p/vlc4p9RB9W). "Semi" because it relies on the timer delay which is , well, just not good.

I'm not sure why this version (http://play.golang.org/p/Ey5eOubKpF) not working- I create a buffered channel, push values into it and then try to read them. Yet i'm missing something obvious and it i's throwing the "deadlock"

Thank you
AZ

DisposaBoy

unread,
Jul 18, 2012, 4:56:00 PM7/18/12
to golan...@googlegroups.com, Paul Borman


On Wednesday, July 18, 2012 9:33:17 PM UTC+1, Andrew wrote:
Paul,
 thank you for the ideas. It helped.
I ended up with semi working version (http://play.golang.org/p/vlc4p9RB9W). "Semi" because it relies on the timer delay which is , well, just not good.

I'm not sure why this version (http://play.golang.org/p/Ey5eOubKpF) not working- I create a buffered channel, push values into it and then try to read them. Yet i'm missing something obvious and it i's throwing the "deadlock"

Thank you
AZ


It deadlocks because you range over a channel that is never closed on line 34. Since it's never closed, the loop never ends and therefore blocks the  main() goroutine. There's nothing else that writes to the channel after those 100 goroutines end therefore the loop cannot proceed and since it blocks main() your program is deadlocked
 

Chris Hines

unread,
Jul 18, 2012, 5:01:46 PM7/18/12
to golan...@googlegroups.com, Paul Borman


On Wednesday, July 18, 2012 4:33:17 PM UTC-4, Andrew wrote:
Paul,
 thank you for the ideas. It helped.
I ended up with semi working version (http://play.golang.org/p/vlc4p9RB9W). "Semi" because it relies on the timer delay which is , well, just not good.

I'm not sure why this version (http://play.golang.org/p/Ey5eOubKpF) not working- I create a buffered channel, push values into it and then try to read them. Yet i'm missing something obvious and it i's throwing the "deadlock

This loop: 

for i := range ch {
ms.counter = ms.counter + i
}

will not exit until the channel ch is closed.

Chris 

Thank you
AZ


On Wed, Jul 18, 2012 at 12:57 PM, Paul Borman wrote:
If it is an int type look at the sync/atomic package.  If it is more complicated then you can use a sync.Mutex.

func (ms *MyStruct) IncCounter() int32 {
    return atomic.AddInt32(&ms.counter, 1)
}

func (ms *MyStruct) IncCounter() int {
    ms.mu.Lock()
    defer ms.mu.Unlock()
    ms.counter++
    return ms.counter
}

or, as in your direction, fire off a goroutine that reads a channel stored with ms.  If you need the value it incremented to then you can make a channel that takes something like:

type Request type {
    Delta MyType
    C chan MyType
}

and end up doing:

r := Request{Delta: value; make(chan MyType)}
ms.ch <- r
newValue := <- r.C

Hope this gives you some ideas

    -Paul

PS:  All the code above was typed into the mail not actually run.

Andrew Z

unread,
Jul 18, 2012, 5:35:48 PM7/18/12
to Paul Borman, golan...@googlegroups.com

i took an example from the "Effective go" and modified it by removed the explicit "close" http://play.golang.org/p/lAY7r4t_lr
now i enjoy seeing the deadlock on receiving channel.

So i _must explicitly_ close the channel for "Range" to stop reading on the channel? If that's true then how about the sutaiton when i have multiple routines running in parallel and having diffferent execution time... I would not know when the last one finished ...
what's the solution in this case? do i have to build a tracking mechanism just to identify that last routine has completed and i can close the channel?
please advise.
AZ

Rémy Oudompheng

unread,
Jul 18, 2012, 5:49:37 PM7/18/12
to Andrew Z, Paul Borman, golan...@googlegroups.com
On 2012/7/18 Andrew Z <for...@gmail.com> wrote:
> i took an example from the "Effective go" and modified it by removed the
> explicit "close" http://play.golang.org/p/lAY7r4t_lr
> now i enjoy seeing the deadlock on receiving channel.
>
> So i _must explicitly_ close the channel for "Range" to stop reading on the
> channel? If that's true then how about the sutaiton when i have multiple
> routines running in parallel and having diffferent execution time... I would
> not know when the last one finished ...
> what's the solution in this case? do i have to build a tracking mechanism
> just to identify that last routine has completed and i can close the
> channel?

You have to either use a sync.WaitGroup (or any other "tracking
system"), or find another to know that the goroutines have finished.
Goroutines are supposed to live independently and cannot know about
each other's status unless they communicate with something you setup.

Rémy.

tomwilde

unread,
Jul 18, 2012, 5:50:39 PM7/18/12
to golan...@googlegroups.com
Use defer

Andrew Z

unread,
Jul 18, 2012, 6:00:59 PM7/18/12
to Rémy Oudompheng, Paul Borman, golan...@googlegroups.com

but why putting range into gor outine works ?
http://play.golang.org/p/4wZhG7y38r

Steven Blenkinsop

unread,
Jul 18, 2012, 7:10:05 PM7/18/12
to Andrew Z, Rémy Oudompheng, Paul Borman, golan...@googlegroups.com
On Wednesday, July 18, 2012, Andrew Z wrote:

but why putting range into gor outine works ?
http://play.golang.org/p/4wZhG7y38r 

When your main routine eventually exits, the goroutine hanging in the range loop is killed, so you don't get a deadlock. If this were a long running program, your goroutine would be hanging around for a long time. The way to do this if you have multiple sending goroutines is to have a goroutine wait on a sync.WaitGroup and close the channel when all the goroutines are done.

chl

unread,
Jul 18, 2012, 10:02:21 PM7/18/12
to golan...@googlegroups.com
I would do your original program like this:

Andrew Z

unread,
Jul 19, 2012, 12:38:44 AM7/19/12
to Steven Blenkinsop, Rémy Oudompheng, Paul Borman, golan...@googlegroups.com
ohhh. what a shame... It was all in front of my eyes right from the beginning (Paul pointed me to this) and yet it took so many replies for me to finally "get it",

Thank you gentlemen for all your help!

That's what i ended up with (http://play.golang.org/p/WFt-xzcu-q)

But the rabbit's hole is deep :)
Now if i change the task slightly and will need to assign the initial value of the struct's counter to local counter, before  increasing it:

from :
 unc (ms *MyStruct) IncCounter(chl chan int) {
    lclcounter := lclcounter + 1
    chl <- lclcounter
    wg.Done()
}

to something like:
func (ms *MyStruct) IncCounter(chl chan int) {
    lclcounter := ms.counter
    lclcounter = lclcounter + 1
    chl <- lclcounter
    wg.Done()
}

than the question becomes - how do i insure that all parallel processes will refer to the correct _initial_ value of the struct's variable?

One way of doing it is to create a temporary variable and send it to the goroutines:
TmpCounter := ms.counter
for ....
 go ms.IncCounter(ch,TmpCounter)
,,,

but what is the better way?

// I guess i'm looking for "set transaction ..." :)

Thank you
AZ

andrey mirtchovski

unread,
Jul 19, 2012, 12:56:33 AM7/19/12
to Andrew Z, golan...@googlegroups.com
> than the question becomes - how do i insure that all parallel processes will
> refer to the correct _initial_ value of the struct's variable?

you're probably looking for once.Do:

http://golang.org/pkg/sync/#example_Once

DisposaBoy

unread,
Jul 19, 2012, 1:33:52 AM7/19/12
to golan...@googlegroups.com, Steven Blenkinsop, Rémy Oudompheng, Paul Borman


On Thursday, July 19, 2012 5:38:44 AM UTC+1, Andrew wrote:
ohhh. what a shame... It was all in front of my eyes right from the beginning (Paul pointed me to this) and yet it took so many replies for me to finally "get it",

Thank you gentlemen for all your help!

That's what i ended up with (http://play.golang.org/p/WFt-xzcu-q)

But the rabbit's hole is deep :)
Now if i change the task slightly and will need to assign the initial value of the struct's counter to local counter, before  increasing it:
[...]
 
there's a myriad ways to do something like this depending on what you're doing, here are three simple approaches:

since the number of expected values is constant, you could simple collect said constant number of values e.g. http://play.golang.org/p/WxITDPf1X0

or you can put the consumer in its own goroutine and close the channel, thus signalling that it should finish up what it's doing http://play.golang.org/p/5-kDiknzjQ , this version uses a second waitgroup to wait for the consumer which can be simpler in the case that you may have multiple consumers

another version that waits for the comsumer using a result channel which may be clearer for this single consumer case http://play.golang.org/p/2NdEHXW756 



Andrew Z

unread,
Jul 19, 2012, 7:57:00 AM7/19/12
to andrey mirtchovski, golan...@googlegroups.com

andrey,
not sure - i want to pass same initial value to multiple goroutines. "do" seemed to serve for single call. like init .

Reply all
Reply to author
Forward
0 new messages