Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go并发编程小测验: 你能答对几道题? #101

Open
smallnest opened this issue Apr 28, 2019 · 27 comments

Comments

Projects
None yet
@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

  1. D
  2. D
  3. D
  4. B
  5. D
  6. 不熟悉Pool,不知道……
  7. C
  8. D
  9. A
  10. B

另:求正确答案……

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented Apr 29, 2019

正确答案加上了,回答的还不错。

@PureWhiteWu

  1. D
  2. D
  3. D
  4. B
  5. D
  6. 不熟悉Pool,不知道……
  7. C
  8. D
  9. A
  10. B

另:求正确答案……

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

鸟哥求教一下,为什么第四题是C呢? @smallnest

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

我感觉因为有mutex的保护,所以f一定只会被执行一次

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented Apr 29, 2019

因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented Apr 29, 2019

IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

我认为在这里问题并不是happens before,而是缓存一致性。
Lock可以保证拿到锁的goroutine的write一定happens before 其它goroutine的19行的read。
但是由于其它goroutine在13行有读过done,所以可能会存在cpu的缓存中,导致19行中read是从CPU缓存读取的,就会导致脏读。

@smallnest
IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented Apr 29, 2019

你的理解是对的,通过对第13行,第19行使用atomic,可以给它们之间建立一个happen before的关系,保证写对读可见,否则也可以把第13行放在Lock的临界区中,但是性能就不保证了。

Go的内存模型就是定义happen before,保证某一个时刻的w,确保r能读取到。

@PureWhiteWu
我认为在这里问题并不是happens before,而是缓存一致性。
Lock可以保证拿到锁的goroutine的write一定happens before 其它goroutine的19行的read。
但是由于其它goroutine在13行有读过done,所以可能会存在cpu的缓存中,导致19行中read是从CPU缓存读取的,就会导致脏读。

@smallnest
IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

感谢大佬抽空指点!
经常看大佬博客,受益匪浅!

@smallnest
你的理解是对的,通过对第13行,第19行使用atomic,可以给它们之间建立一个happen before的关系,保证写对读可见,否则也可以把第13行放在Lock的临界区中,但是性能就不保证了。

Go的内存模型就是定义happen before,保证某一个时刻的w,确保r能读取到。

@PureWhiteWu
我认为在这里问题并不是happens before,而是缓存一致性。
Lock可以保证拿到锁的goroutine的write一定happens before 其它goroutine的19行的read。
但是由于其它goroutine在13行有读过done,所以可能会存在cpu的缓存中,导致19行中read是从CPU缓存读取的,就会导致脏读。

@smallnest
IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

@meilihao

This comment has been minimized.

Copy link

commented Apr 29, 2019

@smallnest 能帮我解读一下第2题吗? mu.Lock()阻塞我能理解, 为什么第二个mu.RLock()也会阻塞?

@PureWhiteWu

This comment has been minimized.

Copy link

commented Apr 29, 2019

@meilihao
@smallnest 能帮我解读一下第2题吗? mu.Lock()阻塞我能理解, 为什么第二个mu.RLock()也会阻塞?

因为go为了防止写锁饥饿,当有写锁等待时,后来的读锁获取不了,会等待写锁完成后再获取读锁。

@LooJee

This comment has been minimized.

Copy link

commented Apr 30, 2019

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

是因为多核cpu缓存不一致吗?

@wshlovercn

This comment has been minimized.

Copy link

commented Apr 30, 2019

第四个,都加锁了,当然可以保证happen before 了。
应该选B

@Shitaibin

This comment has been minimized.

Copy link

commented May 3, 2019

鸟哥,@smallnest 第6题Pool的我跑的结果是这样的,不应该是内存先涨再回收吗:

Cycle 0: 0 MB
Cycle 1: 256 MB
Cycle 2: 513 MB
Cycle 3: 769 MB
Cycle 4: 1281 MB
Cycle 5: 1281 MB
Cycle 6: 1281 MB
Cycle 7: 1537 MB
Cycle 8: 1793 MB
Cycle 9: 2049 MB
Cycle 10: 2049 MB
......
Cycle 107: 14593 MB
Cycle 108: 15105 MB
Cycle 109: 2304 MB
Cycle 110: 0 MB
Cycle 111: 256 MB
Cycle 112: 513 MB
......
@zeast

This comment has been minimized.

Copy link

commented May 7, 2019

@smallnest
你的理解是对的,通过对第13行,第19行使用atomic,可以给它们之间建立一个happen before的关系,保证写对读可见,否则也可以把第13行放在Lock的临界区中,但是性能就不保证了。

Go的内存模型就是定义happen before,保证某一个时刻的w,确保r能读取到。

@PureWhiteWu
我认为在这里问题并不是happens before,而是缓存一致性。
Lock可以保证拿到锁的goroutine的write一定happens before 其它goroutine的19行的read。
但是由于其它goroutine在13行有读过done,所以可能会存在cpu的缓存中,导致19行中read是从CPU缓存读取的,就会导致脏读。

@smallnest
IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

这个并没有缓存一致性的问题,17 行以后的代码都是加锁的,数据肯定是一致的。
Java 加 volatile 是为了禁止指令重排,和这儿没关系。对比官方实现加 atomic 是因为 13 行没有加锁,所以为了原子操作 o.done 才加 atomic,不然会有 data race。
所以这个实现,我觉得虽然有数据竞争,但是 f 肯定也只执行一次。

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented May 8, 2019

我觉得你说的有道理。

我应该把这个例子再设置复杂一点,不使用基本数据类型uint32,而是使用一个复杂的对象。 本意是测试java单例相同的问题:即使字段被赋值了,其他的goroutine也可能得到一个未完全初始化的对象

@zeast

@smallnest
你的理解是对的,通过对第13行,第19行使用atomic,可以给它们之间建立一个happen before的关系,保证写对读可见,否则也可以把第13行放在Lock的临界区中,但是性能就不保证了。

Go的内存模型就是定义happen before,保证某一个时刻的w,确保r能读取到。

@PureWhiteWu
我认为在这里问题并不是happens before,而是缓存一致性。
Lock可以保证拿到锁的goroutine的write一定happens before 其它goroutine的19行的read。
但是由于其它goroutine在13行有读过done,所以可能会存在cpu的缓存中,导致19行中read是从CPU缓存读取的,就会导致脏读。

@smallnest
IA64情况下也是有问题的,因为Lock并没有保护着第13行的读,所以即使本goroutine设置了done =1,其它的goroutine在这个时候还是可能读取到done == 0,所以必须通过atomic保证happen before关系(atomic基本可以认为保证happen before,虽然官方没有明确保证,但是认为目前使用起来还没遇到过问题)

@PureWhiteWu
有道理,之前只考虑了IA64平台上的情况,在其它平台上不能保证没问题,多谢!

@smallnest
因为o.Done没有保护,通过atomic可以保证多CPU情况下不会有问题。 类似的Java的双检查要求变量使用volatile声明。可以和Once的实现代码对照

@PureWhiteWu
鸟哥求教一下,为什么第四题是C呢? @smallnest

这个并没有缓存一致性的问题,17 行以后的代码都是加锁的,数据肯定是一致的。
Java 加 volatile 是为了禁止指令重排,和这儿没关系。对比官方实现加 atomic 是因为 13 行没有加锁,所以为了原子操作 o.done 才加 atomic,不然会有 data race。
所以这个实现,我觉得虽然有数据竞争,但是 f 肯定也只执行一次。

@PureWhiteWu

This comment has been minimized.

Copy link

commented May 9, 2019

https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/golang-nuts/6HlK0Lz2O5Y
结论是,第四题中虽然没有正确实现单例,但是f也最多只能运行一次,而不会运行多次。

@smallnest

This comment has been minimized.

Copy link
Owner Author

commented May 9, 2019

是的,避免误导,答案C我修改了。

目前看来第四题可能存在几个问题:

  1. 非原子操作,在某些架构下是有问题的
  2. 先于f执行标志设置
  3. 有可能一个goroutine看到字段已经被赋值,但是它的值还没有初始化: https://groups.google.com/forum/#!topic/golang-nuts/816FGr-mTUs

代码库中也修改了一下,使用复杂的struct代替基本数据类型int

@PureWhiteWu
https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/golang-nuts/6HlK0Lz2O5Y
结论是,第四题中虽然没有正确实现单例,但是f也最多只能运行一次,而不会运行多次。

@nkbai

This comment has been minimized.

Copy link

commented May 14, 2019

go从哪个版本开始var c chan int可以直接使用,而不是像我最接触的c:=make(chan int,1)?
谢谢

@LooJee

This comment has been minimized.

Copy link

commented May 15, 2019

go从哪个版本开始var c chan int可以直接使用,而不是像我最接触的c:=make(chan int,1)?
谢谢

这个是声明一个channel,没有为它分配内存,要使用它还是需要make的,看下面的闭包函数。

@nkbai

This comment has been minimized.

Copy link

commented May 16, 2019

func TestC(t *testing.T) {
	var c chan int
	go func() {
		<-c
		t.Logf("read finished")
	}()
	c <- 1
	t.Logf("write finished")
}

这种做法,读写都会卡主,而不会崩溃,

@cxzgb123

This comment has been minimized.

Copy link

commented Jun 21, 2019

鸟哥,最后3题能给个分析么,没搞明白是为什么.....

@elvismacak

This comment has been minimized.

Copy link

commented Jul 11, 2019

@cxzgb123
鸟哥,最后3题能给个分析么,没搞明白是为什么.....

  1. map不是协程安全的
  2. slice不是协程安全的
  3. for循环是拷贝
@jiashiran

This comment has been minimized.

Copy link

commented Jul 18, 2019

@smallnest
正确答案加上了,回答的还不错。

@PureWhiteWu

  1. D
  2. D
  3. D
  4. B
  5. D
  6. 不熟悉Pool,不知道……
  7. C
  8. D
  9. A
  10. B

另:求正确答案……

5不正确,是程序阻塞,因为mu2的状态是lock状态

@YJinHai

This comment has been minimized.

Copy link

commented Jul 19, 2019

感谢。要是谁能够给每题答案加上注解就更好了

@LooJee

This comment has been minimized.

Copy link

commented Jul 20, 2019

@smallnest
正确答案加上了,回答的还不错。

@PureWhiteWu

  1. D
  2. D
  3. D
  4. B
  5. D
  6. 不熟悉Pool,不知道……
  7. C
  8. D
  9. A
  10. B

另:求正确答案……

5不正确,是程序阻塞,因为mu2的状态是lock状态

死锁会报 panic dead lock 的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.