Golang needs lock to read an int

為{幸葍}努か 提交于 2021-01-29 12:27:06

问题


In The Go Programming Language, Alan Donovan, page 264 he uses a mutex to read an int.

I don't understand why since an int will fit in a single word, so it cannot be a torn read.

I'm probably wrong, but how? Thanks.

--- Update with code ---

func Balance() int {
  mu.Lock()
  defer mu.Unlock()
  return balance
}

Then down the page

func Withdraw(amount int) bool {
  Deposit(-amount)
  if Balance() < 0 {
    Deposit(amount)
    return false // insufficient funds
  }
}

This design lets the balance become invalid, which can be observed by a reader, which is then fixed in the book with locking in the Withdraw() func.

Apologies really, I think I wasted everyone's time :( I set everyone up to fail answering this correctly.

My theory is that if the balance variable was checked (under lock) that there is sufficient funds before it mutates it, then the locking in the Balance() func would not be needed, but I still may be wrong, especially as people have mentioned reordering which is kind of mysterious.


回答1:


Mutex is there to provide the necessary memory barriers and establish a happened-before relationship:

https://golang.org/ref/mem

The guarantee here is that once the mutex is unlocked, all the other goroutines that access the integer using the same mutex will see the value the integer has after the unlock. Without a mutex, there is no such guarantee; a goroutine may see the value of the int before a writer goroutine wrote to it.

Even though Go memory model does not explicitly say so, atomics guarantee the same as well, provided all goroutines read/write using atomics.




回答2:


If you have a single goroutine, you need no special means to read and write variables.

If you have multiple goroutines, access to variables that are accessed from multiple goroutines and at least one of the accesses is a write must be synchronized.

Quoting from The Go Memory Model:

Within a single goroutine, reads and writes must behave as if they executed in the order specified by the program. That is, compilers and processors may reorder the reads and writes executed within a single goroutine only when the reordering does not change the behavior within that goroutine as defined by the language specification. Because of this reordering, the execution order observed by one goroutine may differ from the order perceived by another. For example, if one goroutine executes a = 1; b = 2;, another might observe the updated value of b before the updated value of a.

Although Go encourages another approach:

Do not communicate by sharing memory; instead, share memory by communicating.

So instead of using locks to protect variables, you should use channels to send new values and computation results to where it needs to be used, so reading shared variables will not be necessary. Channels are safe for concurrent use, data races cannot occur by design.




回答3:


On page 267 it addresses my question, saying "You may wonder why the Balance() method needs mutal exclusion."

It puts it down to CPU caching as @Nikita specifically mentioned in his comment. The author also says there's no such thing as a benign data race which is what @JimB alluded to with his link in another comment.



来源:https://stackoverflow.com/questions/60581304/golang-needs-lock-to-read-an-int

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!