带有 select 语句的程序在 go 中逃脱死锁

Posted

技术标签:

【中文标题】带有 select 语句的程序在 go 中逃脱死锁【英文标题】:Program with select statements escape deadlock in go 【发布时间】:2020-07-14 13:18:26 【问题描述】:

这个问题很可能已经被我找不到了,所以我们开始吧:

我有这个 go 函数,它使用 select 语句发送或接收“消息”,无论哪个可用:

func Seek(name string, match chan string) 
select 
case peer := <-match:
    fmt.Printf("%s sent a message to %s.\n", peer, name)
case match <- name:
    // Wait for someone to receive my message.

我在 4 个不同的 go-routines 上启动这个函数,使用一个无缓冲的通道(使用缓冲区 och 1 会更好,但这只是实验性的):

people := []string"Anna", "Bob", "Cody", "Dave"
match := make(chan string)
for _, name := range people 
    go Seek(name, match, wg)

现在,我刚刚开始使用 go 并认为由于我们使用的是无缓冲通道,因此“select”的发送和接收语句都应该阻塞(没有人在等待发送消息,所以你可以' t 接收,并且没有人等待接收,因此您无法发送),这意味着函数之间不会进行任何通信,也就是死锁。但是运行代码告诉我们情况并非如此:

API server listening at: 127.0.0.1:48731
Dave sent a message to Cody.
Anna sent a message to Bob.
Process exiting with code: 0

我想问你们这些可爱的人,为什么会这样?编译器是否意识到函数想要在同一个通道中读/写并安排它发生?还是“选择”语句会不断检查是否有任何人可以使用该频道?

对不起,如果这个问题很难回答,我还是个新手,对幕后的事情没有经验:)

【问题讨论】:

我猜它开始 goroutines 的引用是你的main() - 你能展示它的其余部分吗?我猜你的应用会在任何事情发生死锁之前退出。 @Adrian 嗨!我不知道发布所有代码的最佳方式,所以我猜this 会这样做(它包含一些我不认为对问题很重要的更改)。问题是我根本不希望程序打印任何东西,我发现程序设法传递任何消息很有趣。 一开始我看错了代码,这不是你的main,而是你的select - 请看我的回答。 【参考方案1】:

现在,我刚刚开始使用 go 并认为由于我们使用的是无缓冲通道,因此“select”的发送和接收语句都应该阻塞(没有人在等待发送消息,所以你可以' t 收到,没有人等着收到,所以你不能发送)

这其实不是真的;实际上,有多个 goroutine 等待接收和多个 goroutine 等待发送。当一个 goroutine 像你一样执行 select 时:

select 
case peer := <-match:
    fmt.Printf("%s sent a message to %s.\n", peer, name)
case match <- name:
    // Wait for someone to receive my message.

它同时等待发送和接收。由于您有多个例程执行此操作,因此每个例程都会找到发送者和接收者。什么都不会阻挡。 selects 将随机选择cases,因为多个案例同时被解锁。

【讨论】:

好吧,这很有意义。我认为主要问题是我不太了解 select 语句的操作方式。你能说两行代码(发送和接收)同时运行,并且最先解除阻塞的那一行是实际执行的那一行吗? (类比不是很好,因为它可能需要向自身发送消息的功能)。非常感谢! 这是一种思考方式。 per the spec 实际发生的情况是,除了通道操作之外,所有这些都是按顺序执行的。然后评估所有通道操作;如果恰好有一个被解除阻塞,则执行;如果多个未阻塞,则随机选择一个并执行;如果所有都被阻止,它会等到一个被解除阻止。 Go 的一大优势(其简单性的副作用)是规范简短易读。它非常密集,所以像 Tour 和 Effective Go 这样的东西是第一次阅读,但我绝对建议阅读整个规范,它不是很长,而且充满了非常有用的信息。 是的,我想我以前看过它,但我现在才意识到文档的可读性和简洁性。伟大的追索权!

以上是关于带有 select 语句的程序在 go 中逃脱死锁的主要内容,如果未能解决你的问题,请参考以下文章

Go select死锁分析

Go select死锁分析

使用带有选择的通道时的 Goroutine 死锁

同一张表上的两个“SELECT FOR UPDATE”语句会导致死锁吗?

SQL Server函数里面有一个Select语句和事务死锁

mysql insert into select 语句为啥会造成死锁