python转go学习笔记———协程和管道

goroutine(协程)和channel(管道)

go协程的特点
  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级的线程
//从goroutine
func newTask()  {
}

//主goroutine
func main()  {
	//创建一个go程序去执行newTask()流程
	go newTask()
}
  1. 如果主线程退出了,那么协程即使没执行完毕也会退出!
  2. 主线程就是一个物理线程,直接作用于CPU上。
  3. 协程是从主线程开启的,是轻量级的线程,逻辑态,对资源消耗相对小

获取系统cpu的数量

func main() {
	//获取当前系统逻辑CPU的数量
	num := runtime.NumCPU()
	//我这里设置num-1的cpu运行go程序
	runtime.GOMAXPROCS(num)	// 设置可同时执行的最大CPU个数
	fmt.Println("num = ",num)	// num = 8
}

计算1-200的阶乘,使用goroutine

  • 全局互斥锁
var (
	myMap  = make(map[int]int,10)
	//声明一个全局的互斥锁
	//  sync包:
	//
	lock sync.Mutex // Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。
)
// test函数就是计算n!
func test(n int)  {
	res := 1
	for i:=1;i<=n;i++ {
		res *= 1
	}
	lock.Lock()
	myMap[n] = res	// concurrent map writes
	lock.Unlock()
}

func main()  {
	for i:= 0;i<200;i++{
		go test(i)
	}
	time.Sleep(10*time.Second)
	// 输出结果
	for i,v := range myMap{
		fmt.Printf("myMap[%d] = %d\n",i,v)
	}
}
channel(管道)
  1. channel本质上就是一个数据结构–队列
  2. 数据是先进先出
  3. 线程安全,多goroutine访问时,不需要加锁,channel本身就是线程安全的
  4. channel时又类型的,一个string的channel只能存放string类型的数据
//初始化:var 变量名 chan 数据类型
var intChan chan int
intChan = make(chan int,3)
strChan := make(chan string)

// 向管道写入数据
intChan <- 10
num := 666
intChan <- num

// 向管道接收数据
str := <-strChan
var num1 int
num1 = <- intChan

//扔出去一个数据(不接收某个数据)
<- intChan
  1. channel必须是引用类型
  2. channel必须初始化才能写入数据(先make)
//当channel存入任意数据类型时,取出的数据需要类型断言
type Cat struct {
	Name string
	Age int
}

func main()  {
	allChan := make(chan interface{},10)
	cat := Cat{"Tom",18}
	allChan <- cat
	cat11 := <- allChan
	//fmt.Println(cat11.Name) 错误写法!
	newcat := cat11.(Cat)
	fmt.Println(newcat)
}//{Tom 18}

channel的关闭

使用内置函数close可以关闭channel,关闭后的channel不能写入数据,但是可以读取数据。

channel的遍历

使用for–range进行遍历,但是遍历时channel必须关闭,否则会报deadlock错误;遍历完channel就会退出遍历。

func main()  {
	intChan := make(chan int,100)
	for i:=0;i<100;i++{
		intChan <- i * 2
	}
	close(intChan)
	for v := range intChan{
		fmt.Println(v)
	}
}

例:一个协程用来将数据写入管道,一个协程用来遍历管道取得数据

func writeData(intchan chan int) {
	for i:=0;i<50;i++{
		intchan <- i*2
		fmt.Println("writeData:",i*2)
	}
	close(intchan)
}

func readData(intchan chan int,exitChan chan bool) {
	for {
		v,ok := <-intchan
		if !ok {
			break
		}else {
			fmt.Println("已经读到数据",v)
		}
	}

	for v := range intchan {
		fmt.Println(v)
	}
	//读完数据后,任务完成
	exitChan <- true
	close(exitChan)
}

func main()  {
    // 思路是创建两个管道,一个管道去写数据,一个管道用做判断数据是否读取完毕。
    // 如果完毕,则关闭主线程
	intChan := make(chan int,50)
	exitChan := make(chan bool,1)
	go writeData(intChan)
	go readData(intChan,exitChan)

	//time.Sleep(time.Second*10)
	for {
		_,ok := <-exitChan
		if !ok{
			break
		}
	}
}

阻塞

channel是没有缓冲的,如果num:=<-c先进行,那么main go就会阻塞等待c<-666后才能继续运行
反之,c<-666先进行也会进入阻塞状态

你可能感兴趣的:(GO,golang,python,go,python转go,go语言)