goroutine和channel

goroutine

golang原生支持并发,在golang中每一个并发单元叫goroutinegoroutine你可以理解为golang实现轻量级的用户态线程。go程序启动的时候 其主函数就开始在一个单独的goroutine中运行,这个goroutine我们叫main goroutine。

在golang中启动一个goroutine的成本很低,通常只需要在普通函数执行前加go关键字就可以。

func fn(){
    fmt.Println("hello world")
} 

fn()
go fn() //启用新的goroutine中执行 fn函数

channel

channel是golang中goroutine通讯的一直机制,每一个channel都带有类型,channel使用make来创建。

ch1 :=make(chan int) //创建不带缓冲区的channel
ch2 :=make(chan int,10)//创建带缓冲区的channel

channel是指针类型,它的初始值为nil,channel的操作主要有 发送,接受,和关闭。

ch <- x  // 发送x
x := <-ch // 从channel中接收 然后赋值给x
<-ch     // 接收后丢弃
close(x) //

不带缓存的channel,发送的时候会阻塞当前goroutine,知道channel的信息被其他goroutine消费。 带缓存的channel,当这个channel的缓存队列没有满是往channel写数据是不会阻塞的,当队列满是会阻塞这个goroutinechannel读取的时候如果channel队列有值会读取,队列为空的时候会塞这个goroutine直到channel有值可以读取。 当一个channel被close后,基于该channel的发送操作都将导致panic,接收操作可以接受到已经channel队列里头数据,channel队列为空时产生一个零值的数据。

golang中的channel还可以带方向。

var out chan<- int  //只发送,不能接收
var in <-chan int   //只接收,不能发送 注意 对只接收的channel close会引起编译错误

select控制结构和channel

select是golang中的一个控制结构,和switch表达式有点相似,不同的是select的每个case分支必须是一个通信操作(发送或者接收)。 select随机选择可执行case分支。如果没有case分支可执行,它将阻塞,直到有case分支可执行。 带default分支的select在没有可执行case分支时会执行default

ch :=make(chan int,1)
select {
    case ch <- 1  :
     //代码     
    case n:= <-ch  :
       //代码   
    default : //default 是可选的
       //代码   
}

select语句的特性:

  • 每个case分支都是通信操作
  • 所有case分支表达式都会被求值
  • 如果任意某个case分支不阻塞,它就执行,其他被忽略。
  • 如果有多个case分支都不阻塞,select会随机地选出一个执行。其他不会执行。 否则:
    1. 如果有 default ,则执行该语句。
    2. 如果没有 defaultselect 将阻塞,直到某个case分支可以运行;
ch := make(chan int, 1) //这边需要使用1个缓冲区,这样子可以在一个goroutine内使用

for {
	select {
	case ch <- 1:
		fmt.Println("send")
	case n := <-ch:
		fmt.Println(n)
	default:
		fmt.Println("dd")
	}
	time.Sleep(1e9)
}

本小节简介了goroutinechannel相关概念,具体的并发编程模型将在下个章节详细探讨。