What's the scope of sync.Mutex when defined locally without struct?

1,588 views
Skip to first unread message

Gyu-Ho Lee

unread,
Nov 5, 2015, 10:50:33 PM11/5/15
to golang-nuts
Hello,

Go documentation explains:

type Mutex

type Mutex struct {
        // contains filtered or unexported fields
}

A Mutex is a mutual exclusion lock. Mutexes can be created as part of other structures; the zero value for a Mutex is an unlocked mutex.


But I cannot find any documentation about exact scope of sync.Mutex, and to what extent
it locks and unlocks the data. What if I want to use mutex without struct? Or am I supposed
to use it only with struct? Sometimes I want to have memory unsafe data structures(slice, map)
locally and update it with concurrency without defining structs. Here's my example.
func main() {
var hits struct {
sync.Mutex
n int
}
hits.Lock()
hits.n++
hits.Unlock()
fmt.Println(hits)

m := map[string]time.Time{}

// without this:
// Found 1 data race(s)
var mutex sync.Mutex

done := make(chan struct{})
for range []int{0, 1} {
go func() {
mutex.Lock()
m[time.Now().String()] = time.Now()
mutex.Unlock()
done <- struct{}{}
}()
}
cn := 0
for range done {
cn++
if cn == 2 {
close(done)
}
}
fmt.Println(m)
}


This code has race conditions without mutex. Does that mean sync.Mutex protects the scope of the mutex itself defined?
In this code, mutex take effect in the scope of main function, right? Could anybody point me to the related documentation?

Thanks,

Ian Lance Taylor

unread,
Nov 5, 2015, 11:29:23 PM11/5/15
to Gyu-Ho Lee, golang-nuts
On Thu, Nov 5, 2015 at 7:50 PM, Gyu-Ho Lee <gyu...@gmail.com> wrote:
>
> Go documentation explains:
>
> type Mutex
>
> type Mutex struct {
> // contains filtered or unexported fields
> }
>
> A Mutex is a mutual exclusion lock. Mutexes can be created as part of other
> structures; the zero value for a Mutex is an unlocked mutex.
>
>
> But I cannot find any documentation about exact scope of sync.Mutex, and to
> what extent
> it locks and unlocks the data. What if I want to use mutex without struct?
> Or am I supposed
> to use it only with struct? Sometimes I want to have memory unsafe data
> structures(slice, map)
> locally and update it with concurrency without defining structs.

You are looking at this incorrectly in some way that I don't
understand. A sync.Mutex is a value with two methods: Lock and
Unlock. Lock acquires a lock on the mutex. Unlock releases it. Only
one goroutine can acquire the lock on the mutex at a time.

That's all there is. A mutex doesn't have a scope. It can be a field
of a struct but it doesn't have to be. A mutex doesn't protect
anything in particular by itself. You have to write your code to call
Lock, do the protected operations, and then call Unlock.

Your example code looks fine.

Ian

Gyu-Ho Lee

unread,
Nov 5, 2015, 11:38:52 PM11/5/15
to golang-nuts, gyu...@gmail.com
Thanks Ian!

I always use mutex with structs but wanted to see why and how it works.

> A sync.Mutex is a value.
Only one goroutine can acquire the lock on the mutex at a time.

I think this explanation answers my question.



What I mean by scope is ...

var A struct {
   aa sync.Mutex
   a int
}

var B struct {
   bb sync.Mutex
   b int
}

A.aa.Lock() only locks struct A, not B.
B.bb.Lock() only locks struct B, not A.

This makes sense if I look at mutex just as a value.
Thanks for clarification. I thought mutex was something other than value.

Chris Kastorff

unread,
Nov 5, 2015, 11:47:32 PM11/5/15
to Gyu-Ho Lee, golang-nuts
Putting a mutex in a struct like that is called struct embedding, and is NOT inheritance or mixins or anything else from other languages. It's just another field with automatically written methods that pass through to the inner object, called "promoted methods" in the spec. The mutex has no knowledge of what it's embedded in, nor does any other embedded object; that makes it far, far simpler than inheritance, for better and for worse. (There are also "promoted fields", which are a tiny bit more magical, but irrelevant here, since sync.Mutex doesn't export any fields.) See the spec for details: https://golang.org/ref/spec#Struct_types

Scope only affects the garbage collector. Your use of the an extra variable for a mutex like that is totally fine.

A style nitpick: I'd declare the mutex just above what it protects, without any newlines in between, to make it easier to read (both in structs and outside of them.) If reordering like that isn't reasonable, a comment about what the mutex protects will do.

As a personal style, I usually don't use struct embedding for sync.Mutex, instead preferring to name it explicitly to avoid exporting (promoting) Lock and Unlock, like this:

    type Thing struct {
        UnprotectedField string

        mu sync.Mutex
        protectedField map[string]OtherThing
    }

You'll also see this style in the standard library, like in net/http's conn type: https://golang.org/src/net/http/server.go#L123

On Thu, Nov 5, 2015 at 7:50 PM, Gyu-Ho Lee <gyu...@gmail.com> wrote:

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Gyu-Ho Lee

unread,
Nov 5, 2015, 11:52:51 PM11/5/15
to golang-nuts, gyu...@gmail.com
Great. It's a great tip. Thanks Chris!

Roberto Zanotto

unread,
Nov 6, 2015, 11:01:51 AM11/6/15
to golang-nuts
As a side note, you don't need a mutex to protect a counter, you can use atomic operations as an alternative.
Reply all
Reply to author
Forward
0 new messages