1、阅读下例代码,从以下四个选项中选择代码执行结果(Mutex相关)。
package mainimport ("fmt""sync")var mu sync.Mutexvar chain stringfunc main() {chain = "main"A()fmt.Println(chain)}func A() {mu.Lock()defer mu.Unlock()chain = chain + " --> A"B()}func B() {chain = chain + " --> B"C()}func C() {mu.Lock()defer mu.Unlock()chain = chain + " --> C"}
选项如下:
-
不能编译
-
输出main-->A-->B-->C
-
输出main
-
panic
解析:4。上述代码会产生死锁panic,因为Mutex是互斥锁。
2、阅读下例代码,从以下四个选项中选择代码执行结果(RWMutex相关)。
package mainimport ("fmt""sync""time")var mu sync.RWMutexvar count intfunc main() {go A()time.Sleep(2 * time.Second)mu.Lock()defer mu.Unlock()count++fmt.Println(count)}func A() {mu.RLock()defer mu.RUnlock()B()}func B() {time.Sleep(5 * time.Second)C()}func C() {mu.RLock()defer mu.RUnlock()}
选项如下:
-
不能编译
-
输出 1
-
程序hang住
-
panic
解析:4。上述代码会产生死锁panic,根据sync/rwmutex.go中的注释可以知道,读写锁当有一个协程在等待写锁时,其它协程是不能获得读锁,而在A和C中同一个调用链中间需要让出读锁,让写锁优先获取,而A的读锁又要求C调用完成,因此产生死锁。
3、阅读下例代码,从以下四个选项中选择代码执行结果(WaitGroup相关)。
package mainimport ("sync""time")func main() {var wg sync.WaitGroupwg.Add(1)go func() {time.Sleep(time.Millisecond)wg.Done()wg.Add(1)}()wg.Wait()}
选项如下:
-
不能编译
-
无输出,正常退出
-
程序hang住
-
panic
解析:4。WaitGroup在调用Wait之后是不能再调用Add方法的。
4、阅读下例代码,从以下四个选项中选择代码执行结果(双检查实现单例)。
package mainimport "sync"type Once struct {m sync.Mutexdone uint32}func (o *Once) Do(f func()) {if o.done == 1 {return}o.m.Lock()defer o.m.Unlock()if o.done == 0 {o.done = 1f()}}
选项如下:
-
不能编译
-
可以编译,正确实现了单例
-
可以编译,有并发问题,f函数可能会被执行多次
-
可以编译,但是运行程序会panic
解析:3。在多核CPU中,因为CPU缓存会导致多个核心中变量值不同步。
5、阅读下例代码,从以下四个选项中选择代码执行结果(Mutex相关)。
package mainimport ("fmt""sync")type MyMutex struct {count intsync.Mutex}func main() {var mu MyMutexmu.Lock()var mu2 = mumu.count++mu.Unlock()mu2.Lock()mu2.count++mu2.Unlock()fmt.Println(mu.count, mu2.count)}
选项如下:
-
不能编译
-
输出 1,1
-
输出 1,2
-
panic
解析:4。加锁后复制变量,会将锁的状态也复制过来,所以mul其实是已经加锁的状态,再加锁会产生死锁。
6、阅读下例代码,从以下四个选项中选择代码执行结果(Pool相关)。
package mainimport ("bytes""fmt""runtime""sync""time")var pool = sync.Pool{New: func() interface{} {return new(bytes.Buffer)},}func main() {go func() {for {processRequest(1 << 28) // 256MiB}}()for i := 0; i < 1000; i++ {go func() {for {processRequest(1 << 10) // 1KiB}}()}var stats runtime.MemStatsfor i := 0; ; i++ {runtime.ReadMemStats(&stats)fmt.Printf("Cycle %d: %dB\n", i, stats.Alloc)time.Sleep(time.Second)runtime.GC()}}func processRequest(size int) {b := pool.Get().(*bytes.Buffer)time.Sleep(500 * time.Millisecond)b.Grow(size)pool.Put(b)time.Sleep(1 * time.Millisecond)}
选项如下:
-
不能编译
-
可以编译,运行时正常,内存稳定
-
可以编译,运行时内存可能暴涨
-
可以编译,运行时内存先暴涨,但是过一会儿会回收掉
解析:3。在单核CPU中内存可能会稳定在256MB,如果是多核CPU则会暴涨。
7、阅读下例代码,从以下四个选项中选择代码执行结果(channel相关)。
package mainimport ("fmt""runtime""time")func main() {var ch chan intgo func() {ch = make(chan int, 1)ch <- 1}()go func(ch chan int) {time.Sleep(time.Second)<-ch}(ch)c := time.Tick(1 * time.Second)for range c {fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())}}
选项如下:
-
不能编译
-
一段时间后总是输出 #goroutine:1
-
一段时间后总是输出 #goroutine:2
-
panic
解析:3。因ch未初始化,读写channel都会阻塞,之后被第一个协程重新赋值,导致写的ch都会阻塞。
8、阅读下例代码,从以下四个选项中选择代码执行结果(channel相关)。
package mainimport ("fmt")func main() {var ch chan intvar count intgo func() {ch <- 1}()go func() {count++close(ch)}()<-chfmt.Println(count)}
选项如下:
-
不能编译
-
输出 1
-
输出 0
-
panic
解析:4。ch未被初始化,关闭时会报错。
9、阅读下例代码,从以下四个选项中选择代码执行结果(Map相关)。
package mainimport ("fmt""sync")func main() {var m sync.Mapm.LoadOrStore("a", 1)m.Delete("a")fmt.Println(m.Len())}
选项如下:
-
不能编译
-
输出 1
-
输出 0
-
panic
解析:1。sync.Map没有Len方法。
10、阅读下例代码,选择代码执行结果(happens before相关)。
package mainvar c = make(chan int)var a intfunc f() {a = 1<-c}func main() {go f()c <- 0print(a)}
选项如下:
-
不能编译
-
输出 1
-
输出 0
-
panic
解析:2。c<-0会阻塞依赖与f()的执行。
扫码关注公众号,获取更多优质内容。