3 回答

TA貢獻1804條經驗 獲得超2個贊
Go 使用協作式多任務處理;它不使用搶占式多任務處理:計算機多任務處理。您需要讓調度程序有機會在鎖之間運行。例如,通過調用 Gosched(),
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var m *sync.RWMutex
func main() {
m = new(sync.RWMutex)
n := 100
go func() {
for i := 0; i < n; i++ {
write("WA", i)
}
}()
go func() {
for i := 0; i < n; i++ {
write("WB", i)
}
}()
select {}
}
func write(tag string, i int) {
m.Lock()
fmt.Printf("[%s][%s%d]write start \n", tag, tag, i)
time.Sleep(100 * time.Millisecond)
fmt.Printf("[%s][%s%d]write end \n", tag, tag, i)
m.Unlock()
runtime.Gosched()
}
輸出:
[WB][WB0]write start
[WB][WB0]write end
[WA][WA0]write start
[WA][WA0]write end
[WB][WB1]write start
[WB][WB1]write end
[WA][WA1]write start
[WA][WA1]write end
[WB][WB2]write start
[WB][WB2]write end
[WA][WA2]write start
[WA][WA2]write end
[WB][WB3]write start
[WB][WB3]write end
[WA][WA3]write start
[WA][WA3]write end

TA貢獻1854條經驗 獲得超8個贊
這種情況稱為活鎖。
當你調用m.Unlock()
即使兩個 goroutines(A 和 B)正在等待這個鎖被釋放時,調度程序可以自由地喚醒它們中的任何一個以繼續。
看起來 Go 中調度程序的當前實現并沒有快速切換到 goroutine A 以使其足以獲取互斥鎖。在這發生之前,goroutine B 重新獲取互斥鎖。
正如您可能發現的那樣,如果您在time.Sleep
調用后移動調用m.Unlock
A 和 B goroutines 將同時運行。
希望這是有道理的。

TA貢獻1880條經驗 獲得超4個贊
只是為了詳細說明調度,該for循環是一個順序緊密循環。意味著來自該循環的編譯指令將占用整個線程來完成。同時,低級指令的其他位將阻塞,除非它們在循環runtime.Gosched()中間有一些由或 sleep提供的調度周期。這就是為什么他們實際上沒有機會了解sync.Mutex(順便說一句,兩者都應該sync.Mutex在聲明和實例化時):
go func() {
for i := 0; i < n; i++ {
runtime.Gosched()
write("WA", i)
}
}()
go func() {
for i := 0; i < n; i++ {
runtime.Gosched()
write("WB", i)
}
}()
并且 Go 調度程序在指令級別(如 Erlang)不是搶占式的。這就是為什么最好使用通道來編排執行路徑的原因。
注意:我通過艱難的方式(不是低級 Go 編譯器專家)學到了這一點。通道以更干凈的方式在 Go-Routines(和那些額外的周期)上提供編排。換句話說,sync.Mutex應該僅用于監督對事物的訪問;不是為了編排。
- 3 回答
- 0 關注
- 219 瀏覽
添加回答
舉報