Go中的goroute和channel

基础知识

进程和线程

  • 进程
    进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
  • 线程
    线程是进程的一个执行实体,是CPU调度和分配的基本单位,它是比进程更小的能独立运行的基本单位。
  • 协程
    轻量级的线程
    -联系
    一个进程可以创建和撤销多个线程,同一个进程中的多个线程之间可以并发执行。

并发和并行

  • 并发:多线程程序在一个核的CPU上运行
  • 并行:多线程程序在多个核的CPU上运行

goroute

goroute调度模型

在这里插入图片描述

	M:操作系统的线程
	P:上下文
	G:goroute、协程
  • 关系:

      一个P只能执行一个goroute,其余的在等待
    

在这里插入图片描述

  • goroute之间的通信:

      1、全局变量和锁同步
      2、channel
    

channel

  • 初始化
	var a chan int
	a = make(chan int, 10)
  • 读/写数据
	// 写入管道
	var b int
	a <- b

	// 读出管道
	b = <- a
  • 只读/写管道的定义

      只读 var ch <-chan  int
      只写 var ch chan<-   int
    
  • 关闭channel

package main

import(
	"fmt"
)

func testChan() {
	var a chan int
	a = make(chan int, 10)

	for i := 0; i < 10; i++ {
		a <- i
	}

	// 关闭管道
	close(a)

	for {
		var b int
		b,ok:=<-a
		if ok== false{
			fmt.Println("Chan Closed")
			break	
		}

		fmt.Println(b)
	}

}

func main() {
	testChan()
}

  • 管道关闭例子

      管道不关闭,forrnage会等待
      管道关闭后,forrange退出
      检测管道是否关闭 v,ok:=<-ch
    
package main

import (
	"fmt"
)

func calc(taskChan chan int, resChan chan int,
	exitChan chan bool) {
	for v := range taskChan {
		flag := true
		for i := 2; i < v; i++ {
			if v%i == 0 {
				flag = false
				break
			}
		}

		if flag {
			resChan <- v
		}
	}

	fmt.Println("exit")
	exitChan <- true
}

func main() {
	intChan := make(chan int, 1000)
	resultChan := make(chan int, 1000)
	exitChan := make(chan bool, 8)

	go func() {
		for i := 0; i < 10000; i++ {
			intChan <- i
		}

		close(intChan)
	}()

	for i := 0; i < 8; i++ {
		go calc(intChan, resultChan, exitChan)
	}

	//等待所有计算的goroutine全部退出
	go func() {
		for i := 0; i < 8; i++ {
			<-exitChan
			fmt.Println("wait goroute ", i, " exited")
		}
		close(resultChan)
	}()

	for v := range resultChan {
		fmt.Println(v)
	}
}
  • channel同步例子

      一个协程向管道写入,写完关闭管道,向标记管道丢一个东西
      一个协程从管道读数据,读完也向标记管道丢一个东西
      主线程从标记管道读东西,从而实现等待
    
package main

import (
	"fmt"
)

func read(ch chan int, exitCh chan struct{}) {
	for i := 0; i < 10; i++ {
		ch <- i
	}

	close(ch)
	var a struct{}
	exitCh <- a
}

func write(ch chan int, exitCh chan struct{}) {
	for {
		a, ok := <-ch
		if !ok {
			break
		}
		fmt.Println(a)
	}

	var a struct{}
	exitCh <- a
}

func main() {
	ch := make(chan int, 10)
	exitCH := make(chan struct{}, 2)

	go read(ch, exitCH)
	go write(ch, exitCH)

	var total = 0
	for _ = range exitCH {
		total++
		if total == 2 {
			break
		}
	}
}

输出:
0
1
2
3
4
5
6
7
8
9

  • select使用
package main

import (
	"fmt"
	"time"
)

func write(ch1 chan int, ch2 chan int) {
	for i := 0; i < 10; i++ {
		ch1 <- i
		time.Sleep(time.Second)
		ch2 <- i * 2
		time.Sleep(time.Second)
	}
}

func main() {
	ch1 := make(chan int, 10)
	ch2 := make(chan int, 10)

	go write(ch1,ch2)

	for {
		select {
		case v := <-ch1:
			fmt.Println(v)
		case v := <-ch2:
			fmt.Println(v)
		default:
			fmt.Println("get data timeout")
			time.Sleep(time.Second)
		}
	}
}

  • 协程中的recover

      防止协程中出现panic,导致主线程挂掉,对协程中的panic进行捕捉
    
package main

import (
	"fmt"
	"time"
)

func write() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("catch err")
		}
	}()

	var m map[string]int
	m["123"] = 1

}

func main() {
	go write()

	time.Sleep(time.Second)
}

输出:
catch err

发布了32 篇原创文章 · 获赞 16 · 访问量 4695

猜你喜欢

转载自blog.csdn.net/weixin_44879611/article/details/104189196