为什么互斥代码会停止另一个完整的go例程?

为什么互斥代码会停止另一个完整的go例程?,go,mutex,Go,Mutex,问题是: 为什么没有机会进行“[WA]”的围棋程序? 为什么互斥代码会停止另一个完整的go例程 我知道一定有关于它的故事或理论。 请给我一个url供我阅读和学习。这种情况称为活锁 调用m.Unlock()时,即使两个goroutine(A和B)正在等待释放此锁,调度程序也可以自由唤醒其中任何一个以继续 看起来Go中调度器的当前实现并没有切换到goroutine,以足够快的速度获取互斥。在此之前,goroutine B重新获取互斥锁 正如您可能发现的那样,如果您移动时间。Sleep在m之后调用。U

问题是: 为什么没有机会进行“[WA]”的围棋程序? 为什么互斥代码会停止另一个完整的go例程

我知道一定有关于它的故事或理论。
请给我一个url供我阅读和学习。

这种情况称为活锁

调用
m.Unlock()
时,即使两个goroutine(A和B)正在等待释放此锁,调度程序也可以自由唤醒其中任何一个以继续

看起来Go中调度器的当前实现并没有切换到goroutine,以足够快的速度获取互斥。在此之前,goroutine B重新获取互斥锁

正如您可能发现的那样,如果您移动
时间。Sleep
m之后调用。Unlock
调用A和B goroutines将同时运行


希望这是有意义的。

Go使用多任务协作;它不使用抢先多任务:。您需要给调度程序一个在锁之间运行的机会。例如,通过调用Gosched()


@彼得索的回答是正确的。为了详细说明一下调度,for循环是一个顺序紧循环。这意味着来自该循环的编译指令将占用整个线程来完成。同时,低级别指令的其他位将被阻止,除非它们具有由<代码>运行时.GOCHED()/<代码>或休眠,在循环中间提供的某些调度周期。这就是为什么他们实际上没有机会理解
sync.Mutex
(顺便说一句,在声明和实例化时,两者都应该是
sync.Mutex
):

go func(){
对于i:=0;i
Go调度器在指令级不是抢占式的(比如Erlang)。这就是为什么最好使用通道编排执行路径


注意:我是通过艰苦的方式学会这一点的(不是一个低级的Go编译器专家)。频道以一种更干净的方式提供了编排的日常活动(以及那些额外的周期)。换句话说,
sync.Mutex
应该只用于监督对内容的访问;不用于编排。

互斥锁不会停止另一个goroutine。但是对于调度程序安排goroutines的顺序没有保证。要获得正确的解决方案,请观看Rob Pikes的“Go并发模式”:幻灯片:同样对于Gurantees,Go会仔细阅读:“Go内存模型”:这里的行为实际上是您想要的。您希望绑定CPU的goroutine继续执行任务并在CPU缓存处于热状态时完成,而不是到处跳转。感谢您的回答和解释。我认为它们都有道理。我认为这个答案并不完全准确。Go调度程序是部分抢占的,例如,它可以在函数调用时抢占。例如,参见。@克斯特亚:先发制人:抓住而排斥他人。如果调度程序从正在运行的程序中夺取控制权而程序不放弃控制权,则调度程序是抢占式的。合作:与另一个人或其他人一起行动或工作。当程序放弃控制时,如果调度器从正在运行的程序获得控制,则该调度器是协作的。Go调度程序是一个协作调度程序。Go程序显式或隐式放弃控制。函数的作用是:显式地放弃控制。文件I/O和其他阻塞操作,在某些情况下,函数会隐式调用放弃控制。@克斯特亚:奥斯汀的措辞令人遗憾和困惑。谢谢你的解释,这很有意义。虽然我仍然不能确定协作调度器在这个例子中是一个问题。对write的调用会插入一个抢占点,但由于解锁后锁发生得太快,因此在这段时间间隔内控制权不太可能授予另一个goroutine。在preeptive调度器的情况下也是如此。
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()

    // time.Sleep(1 * time.Millisecond)
}
> go version
go version go1.5.2 windows/amd64
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 
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)
    }
}()