为了账号安全,请及时绑定邮箱和手机立即绑定

为什么互斥体代码会停止另一个完整的 go-routine?

为什么互斥体代码会停止另一个完整的 go-routine?

Go
慕斯709654 2021-12-20 16:44:17
var m *sync.RWMutexfunc 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()    // time.Sleep(1 * time.Millisecond)}结果在控制台:go run mutex.go[WB][WB0]写开始[WB][WB0]写结束[WB][WB1]写开始[WB][WB1]写结束[WB][WB2]写开始[WB][WB2] ]write end[WB][WB3]write start[WB][WB3]write end[WB][WB4]write start[WB][WB4]write end[WB][WB5]write start[WB][WB5]write end[WB][WB6]写入开始[WB][WB6]写入结束[WB][WB7]写入开始[WB][WB7]写入结束[WB][WB8]写入开始[WB][WB8]写入结束[ WB][WB9]写开始[WB][WB9]写结束...> go versiongo version go1.5.2 windows/amd64问题是:为什么“[WA]”的go-routine没有机会?为什么互斥体代码会停止另一个完整的 go-routine?我知道一定有关于它的故事或理论。请给我一个阅读和学习的网址。
查看完整描述

3 回答

?
慕婉清6462132

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 


查看完整回答
反对 回复 2021-12-20
?
哔哔one

TA贡献1854条经验 获得超8个赞

这种情况称为活锁。

当你调用m.Unlock()即使两个 goroutines(A 和 B)正在等待这个锁被释放时,调度程序可以自由地唤醒它们中的任何一个以继续。

看起来 Go 中调度程序的当前实现并没有快速切换到 goroutine A 以使其足以获取互斥锁。在这发生之前,goroutine B 重新获取互斥锁。

正如您可能发现的那样,如果您在time.Sleep调用后移动调用m.UnlockA 和 B goroutines 将同时运行。

希望这是有道理的。


查看完整回答
反对 回复 2021-12-20
?
慕村225694

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应该仅用于监督对事物的访问;不是为了编排。


查看完整回答
反对 回复 2021-12-20
  • 3 回答
  • 0 关注
  • 190 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信