1.goroutine
在go语言里面实现并发很容易。
独立运行的任务被称为goroutine,goroutine不同于线程,它的运行表面上看似乎都是在同时运行,但是由于计算机通常只具有有限数量的处理单元,所以,从技术上来说,goroutine并不是真正的同时在运行。其实相当于是在一条时间轴上,go语言会分配时间,让他们轮流运行。至于运行顺序,那将是随机的。
启动goroutine就像调用函数一样简单,只需要在调用的前面加上go就可以了。
func main() {
for i := 0;i <= 5;i++{
go printNum(i)
}
time.Sleep(1 * time.Second)
}
func printNum(i int){
fmt.Println(strconv.Itoa(i))
}
上面这段代码在每次运行时,你都可以看到不同的运行结果,这也证实了运行顺序的随机性。
但是一旦注释掉time.Sleep(1 * time.Second)这句代码,你就得不到任何输出。
因为golang的主函数(其实也是跑在一个goroutine中)并不会等待其他goroutine结束。如果主goroutine结束了,所有其他goroutine都将结束。
2.channel(通道)
和go语言的其他类型一样,你可以将通道用作变量、传递至函数、存储在结构中,或者其它你想做的事情。
通道是负责在多个goroutine之间进行通信的。
创建一个通道使用make函数创建 ,如 c := make(chan int),chan表示是通道类型,int表示这个通道的接受的值类型。
数据使用 <- 来进行传递,放在chan变量的左边表示把chan接收到的值传递给一个变量,放在右边表示将数据传递给chan。
c <- 99 //表示将99传递给chan变量c
v := <- c //表示将chan变量c里面的值赋值给变量v
当在执行发送操作,也就是正在执行将值传递给chan变量的操作时,执行该操作的goroutine任务会处于等待的过程中,无法执行其它操作。但是其它未等待的扔可以继续自由的运行。执行接收操作的也一样,接收的会等待接收到下一个数据以后才会继续执行。
func main() {
c := make(chan int)
for i := 0;i <= 5;i++{
go printNum(i,c)//1.for循环先会创建6个goroutine任务,去执行,对于goroutine的执行是无序的。
}
time.Sleep(200) //4.增加sleep是为了让读者更直观的感受到chan等待时后续代码是不执行的。因为等待,所以第3步发送给chan的数据没有接收者。
for i := 0;i <= 5;i++{
fmt.Println("get " + strconv.Itoa(i))//5.等待结束,开始执行打印
newi := <- c //6.接收到了通道传来的值。但是接收到的6个数据肯定是按通道发送数据的顺序接收到的。但是下面一条语句执行的时机就不一定了
fmt.Println("get end " + strconv.Itoa(newi))7.这个执行肯定是要在第6步执行完成以后,再根据系统分配,随机执行了。但是执行的肯定都是已经接收到值了的。
}
time.Sleep(1 * time.Second)
}
func printNum(i int,c chan int){
fmt.Println("go " + strconv.Itoa(i)) //2.先执行到这里,创建出的6个goroutine会先执行这一句,即使下面一句代码的通道处于等待状态也不影响6个goroutine这句的执行。
c <- i //3.当第一个goroutine执行到这里的时候发现没有通道接收值的时候,就会在这里等待。后面5个goroutine会在这里排队,后面5个排队执行的顺序也要看系统分配。
fmt.Println("go end " + strconv.Itoa(i))7.这个执行肯定是要在第3步执行完成以后,再根据系统分配,随机执行了。但是执行的肯定都是已经发送过值了的。
}
上面这段代码读者运行一下就可以很清楚channel的等待机制。
具体分析可以看上面代码的注释部分。
3.select处理通道
上面介绍channel的时候使用的都是int类型的chan,所以这其实是一种理想情况,真正的使用时,可能会存在不同类型的chan。这个时候我们就可以使用select来处理不同类型的chan了。
func main() {
c := make(chan int)
for i := 0;i <= 5;i++{
go printNum(i,c)
}
timeOut := time.After(time.Second)
for i := 0;i <= 5;i++{
select {
case newi := <- c:
fmt.Println(strconv.Itoa(newi))
case <- timeOut:
fmt.Println("运行超时")
return
}
}
}
func printNum(i int,c chan int){
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
c <- i
}
有疑问加站长微信联系(非本文作者)