21天从Java转向Go之第九天——水滴石穿(gorutine和通道chan)

goroutine

CSP模型

  • (Coummunicating Sequential Process)通信顺序进程,CSP是一个并发模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体。
  • 当一个程序启动时,只有一个goroutine来调用main函数。称它为主goroutine。
  • 语法:在普通的函数或方法调用前加上go关键字前缀。go语句使函数在一个新创建的goroutine中调用。go语句本身的执行立即完成:
f()   //调用f(),需要等待它返回
go f() //新建一个调用f()的goroutine,调用完立即返回
  • 斐波那契数列demo
package main

import (
	"fmt"
	"time"
)

func main() {

	go func() {
		for {
			for _, v := range `_/|\` {
				fmt.Printf("\r%c", v)
				time.Sleep(time.Second)
			}
		}
	}()
	fmt.Println(fib(50))
}

/**
i index 0,1,2,3,...
return v
*/
func fib(i int) int {
	if i < 2 {
		return i
	}
	return fib(i-1) + fib(i-2)
}

通道chan

  • 通道是可以让一个goroutine发送特定值到另一个goroutine的通信机制。每个通道是一个具体类型的导管,叫做通道的元素类型。一个有int类型的通道写为chan int。make函数可以指定容量,创建一个带缓冲的通道。
var ch = make(chan int)
  • 像map一样,通道是一个使用make函数创建的数据结构的引用。当复制或者作为参数传递到一个函数时,复制的是引用。这样调用者和被调用者都是引用同一份数据结构。通道的零值是nil。
  • 同种类型的通道==判断时,返回true
  • 通道两个操作:发送和接收
ch <- x //发送语句
<-ch //接收语句  丢弃结果
y := <-ch //赋值语句中的接收表达式
close(ch) //关闭通道
  • 无缓冲通道(同步通道)上的发送操作将会阻塞,直到另一个goroutine在对应的通道上执行接收操作,这时值传送完成,两个goroutine都可以继续执行。相反,如果接收方先执行接收操作,将阻塞直到另一个goroutine在同一个通道上发送一个值。
  • 单向通道 在函数参数上声明指定类型,传入实参时会隐式转换为参数要求的单向通道类型,但是反过来不行,不能由单向通道类型引用到底层同一个数据结构的chan的类型
chan <- int  //只能发送类型的通道
<-chan int  //只能接收int类型的通道
  • 缓冲通道,在make函数指定容量。缓冲通道上的发送操作在队列的尾部插入一个元素,接收操作从队列的头部移除一个元素。如果通道满了,会阻塞发送操作,通道空了,会阻塞接收操作。(类似于阻塞队列)

  • cap(ch) 获取通道的容量

  • len(ch) 获取通道元素的个数

并行循环

func loop(slices []int) {
	result := make(chan int)
	for _, v := range slices {
		//v的值被所有的匿名函数值共享并且被后续的迭代更新。
		//新的goroutine执行字面量函数,for循环可能已经更新了v,所以当这些goroutine读取f的值时,
		//它们所看到的都是slice的最后一个元素。通过添加显示参数,可以确保当go语句执行的时候,使用f的当前值。
		go func(v int) {   //捕获迭代变量 每次循环产生的函数,其记录的是循环变量的地址,所有循环产生的函数公用这一个值。
			r := v * v
			result <- r
		}(v)
	}
	for _,v := range slices {
		fmt.Println("v的平方:",v,<-result)
	}
}

select case 后面用到时再回头看

原文地址:https://www.cnblogs.com/perkins/p/15629203.html