題目來源:[Go 并發編程小測驗: 你能答對幾道題?](https://colobu.com/2019/04/28/go-concurrency-quizzes/)
### 1 Mutex
~~~go
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.Unlock()
chain = chain + " --> A"
B()
}
func B() {
chain = chain + " --> B"
C()
}
func C() {
mu.Lock()
defer mu.Unlock()
chain = chain + " --> C"
}
~~~
* A: 不能編譯
* B: 輸出 main --> A --> B --> C
* C: 輸出 main
* D: panic
### 2 RWMutex
~~~go
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
~~~go
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 雙檢查實現單例
~~~go
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
~~~go
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
~~~go
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
~~~go
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
~~~go
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
~~~go
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
~~~go
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
## 答案
### 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()`的執行。
- Golnag常見面試題目解析
- 交替打印數組和字母
- 判斷字符串中字符是否全都不同
- 翻轉字符串
- 判斷兩個給定的字符串排序后是否一致
- 字符串替換問題
- 機器人坐標計算
- 語法題目一
- 語法題目二
- goroutine和channel使用一
- 實現阻塞讀的并發安全Map
- 定時與 panic 恢復
- 高并發下的鎖與map讀寫問題
- 為 sync.WaitGroup 中Wait函數支持 WaitTimeout 功能.
- 七道語法找錯題目
- golang 并發題目測試
- 記一道字節跳動的算法面試題
- 多協程查詢切片問題
- 對已經關閉的的chan進行讀寫,會怎么樣?為什么?
- 簡單聊聊內存逃逸?
- 字符串轉成byte數組,會發生內存拷貝嗎?
- http包的內存泄漏