ort "fmt"
import "runtime"
import "time"
func Afuntion(ch chan int) {
fmt.Println("finish")
<-ch
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
ch := make(chan int) //无缓冲的channel
go Afuntion(ch)
time.Sleep(time.Nanosecond * 1000)
fmt.Println("main goroutine")
ch <- 1
}
在线示例:https://www.bytelang.com/o/s/c/9z_uWI5ZumA=
运行结果:
finishmain goroutine
或者 main goroutine
finish
主goroutine和另外一个goroutine的执行顺序是不确定的(对于多核cpu)
package main
import "fmt"
func Afuntion(ch chan int) {
fmt.Println("finish")
<-ch
}
func main() {
ch := make(chan int) //无缓冲的channel
//只是把这两行的代码顺序对调一下
ch <- 1
go Afuntion(ch)
// 输出结果:
// 死锁,无结果
}
在线示例:https://www.bytelang.com/o/s/c/sLL_Cto3k4E=
代码分析:首先创建一个无缓冲的channel, 然后在主协程里面向channel ch 中通过ch<-1命令写入数据,则此时主协程阻塞,就无法执行下面的go Afuntions(ch),自然也就无法解除主协程的阻塞状态,则系统死锁
总结:
对于无缓存的channel,放入channel和从channel中向外面取数据这两个操作不能放在同一个协程中,防止死锁的发生;同时应该先利用go 开一个协程对channel进行操作,此时阻塞该go 协程,然后再在主协程中进行channel的相反操作(与go 协程对channel进行相反的操作),实现go 协程解锁.即必须go协程在前,解锁协程在后.
带缓存channel:
对于带缓存channel,只要channel中缓存不满,则可以一直向 channel中存入数据,直到缓存已满;同理只要channel中缓存不为0,便可以一直从channel中向外取数据,直到channel缓存变为0才会阻塞.
由此可见,相对于不带缓存channel,带缓存channel不易造成死锁,可以同时在一个goroutine中放心使用,
close():
close主要用来关闭channel通道其用法为close(channel),并且实在生产者的地方关闭channel,而不是在消费者的地方关闭.并且关闭channel后,便不可再想channel中继续存入数据,但是可以继续从channel中读取数据.例子如下:
package main
import "fmt"
func main() {
var ch = make(chan int, 20)
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
//ch <- 11 //panic: runtime error: send on closed channel
for i := range ch {
fmt.Println(i) //输出0 1 2 3 4 5 6 7 8 9
}
}
在线示例:https://www.bytelang.com/o/s/c/XBiMiCoE7dc=
channel阻塞超时处理:
goroutine有时候会进入阻塞情况,那么如何避免由于channel阻塞导致整个程序阻塞的发生那?解决方案:通过select设置超时处理,具体程序如下:
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case i := <-c:
fmt.Println(i)
case <-time.After(time.Duration(3) * time.Second): //设置超时时间为3s,如果channel 3s钟没有响应,一直阻塞,则报告超时,进行超时处理.
fmt.Println("timeout")
o <- true
break
}
}
}()
<-o
}
在线示例:https://www.bytelang.com/o/s/c/6V74LnkRLN0=
golang 并发总结:
并发两种方式:sync.WaitGroup,该方法最大优点是Wait()可以阻塞到队列中的所有任务都执行完才解除阻塞,但是它的缺点是不能够指定并发协程数量.
channel优点:能够利用带缓存的channel指定并发协程goroutine,比较灵活.但是它的缺点是如果使用不当容易造成死锁;并且他还需要自己判定并发goroutine是否执行完.
但是相对而言,channel更加灵活,使用更加方便,同时通过超时处理机制可以很好的避免channel造成的程序死锁,因此利用channel实现程序并发,更加方便,更加易用.