Go无缓冲通道(同步通道)的实现
无缓冲的通道又称为阻塞的通道,我们来看一下如下代码片段。
package main import "fmt" func main() { ch := make(chan int) ch <- 1 fmt.Println("发送成功") }
上面这段代码能够通过编译,但是执行的时候会出现以下错误:
deadlock表示我们程序中的 goroutine 都被挂起导致程序死锁了。为什么会出现deadlock错误呢?
因为我们使用ch := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有接收方能够接收值的时候才能发送成功,否则会一直处于等编程客栈待发送的阶段。
上面的代码会阻塞在ch <- 1这一行代码形成死锁,那如何解决这个问题呢?
其中一种可行的方法是创建一个 goroutine 去接收值,例如:
package main import ( "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) ch <- 1 go func() { defer wg.Done() v := <-ch fmt.Println(v) }() close(ch) wg.Wait() }
我们已经开了一个go协程从管道中读去数据了,为什么还会报错呢?
因为当程序执行到 ch <- www.devze.com1时,进已经发生了阻塞,下面的go协程还没有来得及启动。
go的channel在执行写入时会先检查在此之前有没有读取数据的一方已经存在,在读取时会先检查在此之前有没有写入方已经存在。当我们将读的协程先启动,再写入,就可以了,代码如下:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1www.devze.com) ch := make(chan int) go func() { defer wg.Done() v := <-ch fmt.Println(v) }() ch <- 1python close(ch) wg.Wait() }
同理,如果对一个无缓冲通道执行接收操作时,没有任何向通道中发送值的操作那么也会导致接收操作阻塞。
package main import "fmt" func main() { ch := ROHIZVYfptmake(chan int) <-ch fmt.Println("接收成功") }
其中一种可行的方法是创建一个 goroutine 去写入值,例如:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) v := <-ch go func() { defer wg.Done() ch <- 1 }() fmt.Println(v) close(ch) wg.Wait() }
同理,因为当程序执行到 v := <-ch 时,进已经发生了阻塞,下面的go协程还没有来得及启动。
当我们将写的协程先启动,再读取,就可以了,代码如下:package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) go func() { defer wg.Done() ch <- 1 }() v := <-ch fmt.Println(v) close(ch) wg.Wait() }
使用无缓冲通道进行通信将导致发送和接收的 goroutine 同步化。因此,无缓冲通道也被称为同步通道。
同步:两个goroutine1(写入方)、goroutine2(读取方),goroutine1先执行,如果想要再次发送(写入)数据的话,必须等待goroutine2将channel中的数据取出来(读取)之后,才能再次发送。goroutine2先执行,如果想要再次接收数据的话,必须等待goroutine1再次向channel中写入数据之后,才能再次接收。执行顺序(goroutine1,goroutine2,goroutine1,goroutine2…)goroutine1和goroutine2交替执行。示例演示:使用一个无缓冲channel和两个goroutine实现交替打印一个字符串。package main import "fmt" func main() { ch := make(chan int) str := "hello, world" go func() { for { index, ok := <-ch if !ok { break } if index >= len(str) { close(ch) break } fmt.Printf("Goroutine1 : %c\n", str[index]) ch <- index + 1 } }() ch <- 0 for { index, ok := <-ch if !ok { break } if index >= len(str) { close(ch) break } fmt.Printf("Goroutine1 : %c\n", str[index]) ch <- index + 1 } }
到此这篇关于Go无缓冲通道(同步通道)的实现的文章就介绍到这了,更多相关Go无缓冲通道内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论