这是我的尝试翻译,不准确的名词不要介意,明白就好。纯手翻,格式勿怪。
原文:https://golang.google.cn/ref/mem
前言:
Go 的内存机制解决这样一种情况:一个线程从一个变量读到的数据是另一个线程写入的数据,该机制保证这种操作是可靠的。
建议:
多个goroutine纤程一起编辑某个数据时,应该将它们的操作序列化。(就是排队来,译者)
为了序列化操作,采用channel或者 同步程序包如sync and sync/atomic
(这里两行废话,略过)
前置:
在单线程中,读写操作必须表现的与执行顺序一致。为啥这么讲呢,编译器和CPU可能重新对读写操作排序,在单线程中不会改变他们的执行表现。因为这个重新排序,多线程中,一个线程观察到的执行顺序和另一个线程观察到的执行顺序可能不同。比如,一个线程执行a=1,b=2,另一个线程观察到的可能是b的值改变在先。
为了解释清楚这种事情,我们定义了一个词:前置。如果e1在e2之前发生,e1是e2的前置;如果e1既不在e2之前发生也不在e2之后发生,那就是 并发. (就是不能确定具体谁前谁后 )。
在单线程中,前后顺序就是你编写的逻辑顺序。(废话,后面我捡重要的翻译)
一个变量读操作能观察到同一变量的某个写操作 ,必须有下面两件事 :
1.读不能在写之前
2在该次写之后,在读之前,没有其他的写操作。
同步
初始化:
p程序包导入q程序包,q的init 发生在p的init 之前。
main() 函数 发生在所有包的init之后。
线程创建:
线程创建 在 线程执行 之前。
ar a string
func f() {
print(a)
}
func hello() {
a = "hello, world"
go f()
}
线程的销毁:
线程退出 不能在执行完之前
var a string
func hello() {
go func() { a = "hello" }()
print(a)
}
上面print出的a的值是不确定的。甚至有些优化编译器会将这个go func()移除。
channel通讯
原文:A send on a channel happens before the corresponding receive from that channel completes.
channel的发送 发生在 接收完成 之前. (译者:注意 完成)
var c = make(chan int, 10)
var a string
func f() {
a = "hello, world"
c <- 0
}
func main() {
go f()
<-c
print(a)
}
因为channel的接收在发送之后,所以保证了print出正确的结果: “hello, world”
close channel 发生在 管道出值之前。close channel 使channel 出0值。
上面的例子 c<-0 换成 close© ,结果是一样的。
没有缓冲区的channel,接收在 【发送完成】 之前. (译者:注意 完成,前 )
原文:A receive from an unbuffered channel happens before the send on that channel completes.
var c = make(chan int)
var a string
func f() {
a = "hello, world"
<-c
}
func main() {
go f()
c <- 0
print(a)
}
确保打印出预期的"hello, world"。
译者:接收在 发送完成 之前,比前面的例子更难理解。关键在 是否有缓冲区。相当于一个盛物篮,没有盛物篮,是放不了东西的。接收方不放盛物篮,发送方东西无处可放,只能拿着。
下面我写了一个例子 帮助理解:
package main
var c = make(chan int)
var a int
func f() {
a = <-c
}
func main() {
go f()
c <- 10
print(a)
}
输出10
如果是带缓冲区的channel,执行顺序就是不能确保的。(channel中有盛物篮可放)
*The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes.
*
从缓冲区数是C的channe 中,第k个接收 ,发生在 k+c 个发送完成之前。(还是没有空篮子)
var limit = make(chan int, 3)
func main() {
for _, w := range work {
go func(w func()) {
limit <- 1
w()
<-limit
}(w)
}
select{}
}
保证最多有3个工作在执行。
锁:
sync 包实现两种锁 sync.Mutex 和sync.RWMutex.
For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.
这2种锁,Unlock小于Lock 次数时, Unlock的调用 发生在Lock() 之前。
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock() // 这里会堵塞直到上面的Unlock调用完。
print(a)
}
For any call to l.RLock on a sync.RWMutex variable l, there is an n such that the l.RLock happens (returns) after call n to l.Unlock and the matching l.RUnlock happens before call n+1 to l.Lock.
译者:多个RLock可以并发, 在所有的RLock 被RUnlock之前,Lock被阻塞.
var l sync.RWMutex
var a string
func f() {
l.Lock()
a = "hello, world"
print(a, " f\n")
l.Unlock()
}
func f2() {
time.Sleep(time.Second * 1)
print(a, " f2\n")
l.RUnlock()
}
func f3() {
time.Sleep(time.Second * 1)
b := a
print(b, " f3\n")
l.RUnlock()
}
func main() {
a = "help me"
l.RLock()
l.RLock()
go f()
go f2()
go f3()
time.Sleep(time.Second * 3)
}
Once :
只执行一次。多次调用sync.Once 类型的Done(),只有一次被执行,其他的调用被阻塞到 唯一执行的返回 之后。
A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.
var i int
func setup() {
i++
a = "hello, world\n"
}
func doprint() {
once.Do(setup)
print(i, a)
w.Done()
}
func main() {
w.Add(2)
go doprint()
go doprint()
w.Wait()
}
输出:
1hello, world
1hello, world
可以看到 i=1, hello world被输出2次.