无缓冲的通道又称为阻塞的通道,我们来看一下如下代码片段。
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 <- 1时,进已经发生了阻塞,下面的go协程还没有来得及启动。
go的channel在执行写入时会先检查在此之前有没有读取数据的一方已经存在,在读取时会先检查在此之前有没有写入方已经存在。
当我们将读的协程先启动,再写入,就可以了,代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func() {
defer wg.Done()
v := <-ch
fmt.Println(v)
}()
ch <- 1
close(ch)
wg.Wait()
}
同理,如果对一个无缓冲通道执行接收操作时,没有任何向通道中发送值的操作那么也会导致接收操作阻塞。
package main
import "fmt"
func main() {
ch := make(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
}
}