golang 并发题目测试
1 Mutex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
var chain string
func main() {
chain = "main"
A()
fmt.Println(chain)
}
func A() {
mu.Lock()
defer mu.Lock()
chain = chain + " --> A"
B()
}
func B() {
chain = chain + " --> B"
C()
}
func C() {
mu.Lock()
defer mu.Lock()
chain = chain + " --> C"
}
- A: 不能编译
- B: 输出 main –> A –> B –> C
- C: 输出 main
- D: panic
2 RWMutex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main
import (
"fmt"
"sync"
"time"
)
var mu sync.RWMutex
var count int
func 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()
}
- A: 不能编译
- B: 输出 1
- C: 程序hang住
- D: panic
3 Waitgroup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(time.Millisecond)
wg.Done()
wg.Add(1)
}()
wg.Wait()
}
- A: 不能编译
- B: 无输出,正常退出
- C: 程序hang住
- D: panic
4 双检查实现单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package doublecheck
import (
"sync"
)
type Once struct {
m sync.Mutex
done 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 = 1
f()
}
}
- A: 不能编译
- B: 可以编译,正确实现了单例
- C: 可以编译,有并发问题,f函数可能会被执行多次
- D: 可以编译,但是程序运行会panic
5 Mutex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"fmt"
"sync"
)
type MyMutex struct {
count int
sync.Mutex
}
func main() {
var mu MyMutex
mu.Lock()
var mu2 = mu
mu.count++
mu.Unlock()
mu2.Lock()
mu2.count++
mu2.Unlock()
fmt.Println(mu.count, mu2.count)
}
- A: 不能编译
- B: 输出 1, 1
- C: 输出 1, 2
- D: panic
6 Pool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main
import (
"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.MemStats
for 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)
}
- A: 不能编译
- B: 可以编译,运行时正常,内存稳定
- C: 可以编译,运行时内存可能暴涨
- D: 可以编译,运行时内存先暴涨,但是过一会会回收掉
7 channel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var ch chan int
go 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())
}
}
- A: 不能编译
- B: 一段时间后总是输出
#goroutines: 1
- C: 一段时间后总是输出
#goroutines: 2
- D: panic
8 channel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func main() {
var ch chan int
var count int
go func() {
ch <- 1
}()
go func() {
count++
close(ch)
}()
<-ch
fmt.Println(count)
}
- A: 不能编译
- B: 输出 1
- C: 输出 0
- D: panic
9 Map
1
2
3
4
5
6
7
8
9
10
11
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
m.LoadOrStore("a", 1)
m.Delete("a")
fmt.Println(m.Len())
}
- A: 不能编译
- B: 输出 1
- C: 输出 0
- D: panic
10 happens before
1
2
3
4
5
6
7
8
9
10
11
12
package main
var c = make(chan int)
var a int
func f() {
a = 1
<-c
}
func main() {
go f()
c <- 0
print(a)
}
- A: 不能编译
- B: 输出 1
- C: 输出 0
- D: panic
11 自定义Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main
import "sync"
type Map struct {
m map[int]int
sync.Mutex
}
func (m *Map) Get(key int) (int, bool) {
m.Lock()
defer m.Unlock()
i, ok := m.m[key]
return i, ok
}
func (m *Map) Put(key, value int) {
m.Lock()
defer m.Unlock()
m.m[key] = value
}
func (m *Map) Len() int {
return len(m.m)
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
m := Map{m: make(map[int]int)}
go func() {
for i := 0; i < 10000000; i++ {
m.Put(i, i)
}
wg.Done()
}()
go func() {
for i := 0; i < 10000000; i++ {
m.Len()
}
wg.Done()
}()
wg.Wait()
}
- A: 不能编译
- B: 可运行,无并发问题
- C: 可运行,有并发问题
- D: panic
12 slice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(2)
var ints = make([]int, 0, 1000)
go func() {
for i := 0; i < 1000; i++ {
ints = append(ints, i)
}
wg.Done()
}()
go func() {
for i := 0; i < 1000; i++ {
ints = append(ints, i)
}
wg.Done()
}()
wg.Wait()
fmt.Println(len(ints))
}
- A: 不能编译
- B: 输出2000
- C: 输出可能不是2000
- D: panic
13 goroutine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
import (
"fmt"
"sync"
"time"
)
type T struct {
V int
}
func (t *T) Incr(wg *sync.WaitGroup) {
t.V++
wg.Done()
}
func (t *T) Print() {
time.Sleep(1e9)
fmt.Print(t.V)
}
func main() {
var wg sync.WaitGroup
wg.Add(10)
var ts = make([]T, 10)
for i := 0; i < 10; i++ {
ts[i] = T{i}
}
for _, t := range ts {
go t.Incr(&wg)
}
wg.Wait()
for _, t := range ts {
go t.Print()
}
time.Sleep(5 * time.Second)
}
- A: 输出12345678910
- B: 输出0123456789
- C: 输出9999999999
- D: 输出10101010101010101010
答案
1.D
会产生死锁panic
,因为Mutex
是互斥锁。
2.D
会产生死锁panic
,根据sync/rwmutex.go
中注释可以知道,读写锁当有一个协程在等待写锁时,其他协程是不能获得读锁的,而在A
和C
中同一个调用链中间需要让出读锁,让写锁优先获取,而A
的读锁又要求C
调用完成,因此死锁。
3.D
WaitGroup
在调用 Wait
之后是不能再调用 Add
方法的。
4.C
在多核CPU中,因为CPU缓存会导致多个核心中变量值不同步。
5.D
加锁后复制变量,会将锁的状态也复制,所以mu1
其实是已经加锁状态,再加锁会死锁。
6.C
个人理解,在单核CPU中,内存可能会稳定在256MB
,如果是多核可能会暴涨。
7.C
因为 ch
未初始化,写和读都会阻塞,之后被第一个协程重新赋值,导致写的ch
都阻塞。
8.D
ch
未有被初始化,关闭时会报错。
9.A
sync.Map
没有 Len
方法。
10.B
c <- 0
会阻塞依赖于 f()
的执行。
11.C
12.C
13.C