
本文旨在帮助开发者理解和解决Go并发编程中常见的死锁问题。通过分析一个包含三个并发goroutine互相通信的示例代码,我们将深入探讨死锁产生的原因,并提供一种通过引入缓冲通道和runtime.Gosched()来避免死锁的有效方法。本文还将强调并发程序设计中确定性和避免忙等待的重要性。
在Go语言中,死锁通常发生在多个goroutine因相互等待对方释放资源而无限期阻塞的情况下。在提供的示例代码中,三个goroutine Routine1、Routine2和Routine3通过channel进行通信。每个goroutine都试图从channel接收数据,并可能向其他channel发送数据。如果goroutine之间的channel操作顺序不当,就可能导致死锁。
具体来说,如果一个goroutine在尝试从一个空的channel接收数据时被阻塞,而同时其他goroutine也在等待该goroutine发送数据,那么就会形成一个循环等待的局面,从而导致死锁。
以下是一些可以有效避免死锁的方法:
使用缓冲通道:缓冲通道允许channel在没有接收者的情况下存储一定数量的值。这可以避免goroutine因等待发送数据而被立即阻塞。通过在创建channel时指定缓冲区大小,可以提高程序的并发性和容错性。
command12 := make(chan int, 10) // 创建一个缓冲区大小为10的channel
注意: 缓冲区大小的选择需要根据具体应用场景进行权衡。过小的缓冲区可能无法有效缓解阻塞,而过大的缓冲区则可能浪费内存。
这本书并不是一本语言参考书,但它是一个Android开发者去学习Kotlin并且使用在自己项目中的一个工具。我会通过使用一些语言特性和有趣的工具和库来解决很多我们在日常生活当中都会遇到的典型问题。 这本书是非常具有实践性的,所以我建议你在电脑面前跟着我的例子和代码实践。无论何时你都可以在有一些想法的时候深入到实践中去。 这本书适合你吗? 写这本书是为了帮助那些有兴趣 使用Kotlin语言来进行开发的Android开发者。 如果你符合下面这些情况,那这本书是适合你的: 你有相关Android开发和Andro
11
使用runtime.Gosched():runtime.Gosched()函数可以让当前goroutine放弃执行,允许其他goroutine运行。这可以避免某个goroutine长时间占用CPU资源,从而导致其他goroutine无法及时执行。
在select语句中添加一个default case,并调用runtime.Gosched(),可以确保即使没有channel操作准备好,goroutine也不会无限期阻塞。
select {
case cmd1 := <-response12:
{
// ...
}
case cmd2 := <-response13:
{
// ...
}
default:
runtime.Gosched() // 放弃执行,让其他goroutine运行
}以下是修改后的示例代码,使用了缓冲通道和runtime.Gosched()来避免死锁:
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
func Routine1(command12 chan int, response12 chan int, command13 chan int, response13 chan int) {
rand.Seed(time.Now().UnixNano()) // Seed the random number generator
z12 := 200
z13 := 200
m12 := false
m13 := false
y := 0
for i := 0; i < 20; i++ {
y = rand.Intn(100)
if y == 0 {
fmt.Println(z12, " z12 STATE SAVED")
fmt.Println(z13, " z13 STATE SAVED")
y = 0
command12 <- y
command13 <- y
for m12 != true || m13 != true {
select {
case cmd1 := <-response12:
{
z12 = cmd1
if z12 != 0 {
fmt.Println(z12, " z12 Channel Saving.... ")
y = rand.Intn(100)
command12 <- y
}
if z12 == 0 {
m12 = true
fmt.Println(" z12 Channel Saving Stopped ")
}
}
case cmd2 := <-response13:
{
z13 = cmd2
if z13 != 0 {
fmt.Println(z13, " z13 Channel Saving.... ")
y = rand.Intn(100)
command13 <- y
}
if z13 == 0 {
m13 = true
fmt.Println(" z13 Channel Saving Stopped ")
}
}
default:
runtime.Gosched()
}
}
m12 = false
m13 = false
}
if y != 0 {
if y%2 == 0 {
command12 <- y
}
if y%2 != 0 {
command13 <- y
}
select {
case cmd1 := <-response12:
{
z12 = cmd1
fmt.Println(z12, " z12")
}
case cmd2 := <-response13:
{
z13 = cmd2
fmt.Println(z13, " z13")
}
default:
runtime.Gosched()
}
}
}
close(command12)
close(command13)
}
func Routine2(command12 chan int, response12 chan int, command23 chan int, response23 chan int) {
rand.Seed(time.Now().UnixNano()) // Seed the random number generator
z21 := 200
z23 := 200
m21 := false
m23 := false
for i := 0; i < 20; i++ {
select {
case x, open := <-command12:
{
if !open {
return
}
if x != 0 && m23 != true {
z21 = x
fmt.Println(z21, " z21")
}
if x != 0 && m23 == true {
z21 = x
fmt.Println(z21, " z21 Channel Saving ")
}
if x == 0 {
m21 = true
if m21 == true && m23 == true {
fmt.Println(" z21 and z23 Channel Saving Stopped ")
m23 = false
m21 = false
}
if m21 == true && m23 != true {
z21 = x
fmt.Println(z21, " z21 Channel Saved ")
}
}
}
case x, open := <-response23:
{
if !open {
return
}
if x != 0 && m21 != true {
z23 = x
fmt.Println(z23, " z21")
}
if x != 0 && m21 == true {
z23 = x
fmt.Println(z23, " z23 Channel Saving ")
}
if x == 0 {
m23 = true
if m21 == true && m23 == true {
fmt.Println(" z23 Channel Saving Stopped ")
m23 = false
m21 = false
}
if m23 == true && m21 != true {
z23 = x
fmt.Println(z23, " z23 Channel Saved ")
}
}
}
default:
runtime.Gosched()
}
if m23 == false && m21 == false {
y := rand.Intn(100)
if y%2 == 0 {
if y == 0 {
y = 10
response12 <- y
}
}
if y%2 != 0 {
if y == 0 {
y = 10
response23 <- y
}
}
}
if m23 == true && m21 != true {
y := rand.Intn(100)
response12 <- y
}
if m23 != true && m21 == true {
y := rand.Intn(100)
command23 <- y
}
}
close(response12)
close(command23)
}
func Routine3(command13 chan int, response13 chan int, command23 chan int, response23 chan int) {
rand.Seed(time.Now().UnixNano()) // Seed the random number generator
z31 := 200
z32 := 200
m31 := false
m32 := false
for i := 0; i < 20; i++ {
select {
case x, open := <-command13:
{
if !open {
return
}
if x != 0 && m32 != true {
z31 = x
fmt.Println(z31, " z21")
}
if x != 0 && m32 == true {
z31 = x
fmt.Println(z31, " z31 Channel Saving ")
}
if x == 0 {
m31 = true
if m31 == true && m32 == true {
fmt.Println(" z21 Channel Saving Stopped ")
m31 = false
m32 = false
}
if m31 == true && m32 != true {
z31 = x
fmt.Println(z31, " z31 Channel Saved ")
}
}
}
case x, open := <-command23:
{
if !open {
return
}
if x != 0 && m31 != true {
z32 = x
fmt.Println(z32, " z32")
}
if x != 0 && m31 == true {
z32 = x
fmt.Println(z32, " z32 Channel Saving ")
}
if x == 0 {
m32 = true
if m31 == true && m32 == true {
fmt.Println(" z32 Channel Saving Stopped ")
m31 = false
m32 = false
}
if m32 == true && m31 != true {
z32 = x
fmt.Println(z32, " z32 Channel Saved ")
}
}
}
default:
runtime.Gosched()
}
if m31 == false && m32 == false {
y := rand.Intn(100)
if y%2 == 0 {
response13 <- y
}
if y%2 != 0 {
response23 <- y
}
}
if m31 == true && m32 != true {
y := rand.Intn(100)
response13 <- y
}
if m31 != true && m32 == true {
y := rand.Intn(100)
response23 <- y
}
}
close(response13)
close(response23)
}
func main() {
command12 := make(chan int, 10)
response12 := make(chan int, 10)
command13 := make(chan int, 10)
response13 := make(chan int, 10)
command23 := make(chan int, 10)
response23 := make(chan int, 10)
go Routine1(command12, response12, command13, response13)
go Routine2(command12, response12, command23, response23)
Routine3(command13, response13, command23, response23)
// Wait for a while to allow goroutines to complete
time.Sleep(5 * time.Second)
}代码修改说明:
通过理解死锁产生的原因,并采取适当的措施,可以有效地避免Go并发编程中的死锁问题。缓冲通道和runtime.Gosched()是两种常用的解决方案,但并非银弹。开发者需要根据具体应用场景选择合适的并发模型和同步机制,并仔细测试程序,以确保其正确性和可靠性。 此外,良好的代码设计习惯,例如避免忙等待和保持程序行为的确定性,也是编写高质量并发程序的关键。
以上就是解决Go并发中的死锁问题:深入分析与实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号