Golang 协程 (goroutine) 与通道 (channel)【防备忘】

Golang 协程与通道

  1. 协程
进程:就是程序在操作系统中的一次执行过程
线程:是进程的一个执行实例,比进程更小的能独立运行的基本单位
协程:可以理解为更为轻量级的线程
并行:在多个cpu上,比如一个cpu上有一个线程,在同一时刻,就会有多个线程在同时执行,这就是并行
并发:在一个cpu上,这一个cpu上有多个线程在运行,给你的感觉好像在同时运行,其实,是这多个线程来回切换着执行,实际上,只有一个线程在执行,这就是并发
  1. 不同协程之间的数据通信
(a) 全局变量的互斥锁
(b) 使用管道channel
  1. channel管道
channel的本质就是一个先进先出的队列【FIFO】
channel本身就是安全的,协程操作的时候无须加锁
channel是有类型的,只能存放同一类型的数据
  1. channel的定义/声明
var 变量名 chan 数据类型

数据类型可以是基本类型、指针类型、map、slice,甚至是空接口(可以接收任何类型的变量)
举例:
var intChan chan int
var mapChan map[int]string

说明:
1.channel是引用类型
2.channel必须初始化之后,才能写入,即make之后才能使用
3.channel的容量是固定的
  1. channel应用
var obj int
num := 100
intChan := make(chan int,10)  //初始化(chan 类型,容量)
//写入管道,如果写满了,仍旧再写,会报错
intChan <- 99     
intChan <- num
//读取管道,如果没数据了,仍旧再读,会报错
obj :=<-intChan   //都是合法的

<-intChan  //直接扔数据

  1. channel的关闭
使用内置函数close关闭channel,当channel关闭之后,就不能向channel写数据了,但是仍旧可以从该channel读取数据

intChan := make(chan int,3)
intChan<-100
close(intChan)
intChan<-99  //是错误的
<-intChan   //读是可以的
  1. channel遍历
遍历管道,不能用普通的for,只能用for-range结构
遍历时,如果channel没有关闭,则数据取完的时候,会出现死锁错误
所以,
遍历管道时,应先将channel关闭,则会正常遍历管道,遍历完后,退出遍历。

func main() {
	intChan := make(chan int, 100)

	for i := 0; i < 100; i++ {
		intChan <- i * 2
	}

	close(intChan)
	
	for v := range intChan {
		fmt.Printf("v=%d\n", v)
	}
}
  1. 用管道实现协程之间的通信
package main

import "fmt"

func main() {
	intChan := make(chan int, 50)
	boolChan := make(chan bool, 1)

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

	go func() {
		for v := range intChan {
			fmt.Printf("this is %d!\n", v)
		}
		boolChan <- true
		close(boolChan)
	}()

	for {
		if ok := <-boolChan; ok {
			break
		}
	}
}
  1. 管道阻塞机制
1.如果声明一个通道,容量只有10,若给它一直写入数据,超过了10,与此同时,没有其他的协程来读这个通道的数据,那么就会发生死锁。
2.有一个通道容量为10,如果给它写入的速度快,但是读取的速度慢,这个时候,即使超过了最大容量,也不会死锁,因为管道底层,会检测,只要有人再读这个管道,即使数据超过了,也不会死锁。
  1. 管道使用细节
1.声明通道为只读、只写
var intChan chan<- int   //只写
var intChan <-chan int   //只读
应用:当作协程函数的形参使用,某一个协程只允许写,另一个协程只允许读
func send(intChan chan<- int)
func recv(intChan <-chan int)

2.之前说的,再遍历管道的时候,要先关闭close,是为了防止数据读完的时候,发生死锁,那么又没有一种方法可以不close管道,仍旧可以遍历管道呢?
用select方法
func main() {
	//使用select解决从管道取数据的阻塞问题
	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan <- i
	}

	strChan := make(chan string, 5)
	for j := 0; j < 5; j++ {
		strChan <- fmt.Sprintf("%d", j)
	}

	//实际开发中,我们可能不知道什么时候需要关闭管道
	//使用select解决问题
	for {
		select {
		case v := <-intChan:
			fmt.Printf("intChan:%d\n", v)
		case v := <-strChan:
			fmt.Printf("strChan:%s\n", v)
		default:
			fmt.Println("结束")
			return
		}
	}
}

3.goroutine中使用recover,解决协程中出现的panic,不至于程序整体崩溃,仅仅崩溃这一个协程
func test() {
	defer func() {     
		if err := recover(); err != nil {
			fmt.Println("teste() 发生错误", err)
		}
	}()

	var myMap map[int]string
	myMap[0] = "golang"     //这里有错误
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋山刀名鱼丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值