[golang]golang并发编程-协程

协程,也叫gorountine
go 语言诞生比较晚,web2.0开发逐渐主流,高并发需求大
go 一开始就没有打算让我们去实例化一个线程,而是使用协程

多线程 - 每个线程占用的内存比较多 而且系统切换开销比较大
轻量化线程 -> 协程,是用户级别的
python中有两种编程模式 1.多线程和多进程进行并发编程 2.使用协程进行并发编程

1.goruntine

func p(){
	fmt.Println("bobby")
}
func main(){
	go p()
}

上述代码就是简单的协程
执行结果:什么都没有
原因:主协程先关闭,主死从随

func p(){
	fmt.Println("bobby")
}
func main(){ //主协程  
	//主死从随
	go p()
	time.Sleep(time.Second*2)
}

打印结果:bobby

也可以使用匿名函数

func main(){ //主协程  
	//主死从随
	for i:=0;i < 5;i++{
		go func(){
			fmt.Println(i)
		}
	}
	time.Sleep(time.Second*2)
}

打印结果:5 5 5 5 5

func main(){ //主协程  
	//主死从随
	for i:=0;i < 5;i++{
		//闭包
		go func(){
			fmt.Println(i)
			time.Sleep(time.Second)
		}
	}
	time.Sleep(time.Second*2)
}

打印结果:1 2 3 4 5

一直用time.Sleep去维持主协程肯定不是个事,因此我们来使用WaitGroup:

2.WaitGroup

WaitGroup通了三个很有用的函数:
Add()
Done()
Wait()

var wg sync.WaitGroup
func f(n int){
	defer wg.Done()
	fmt.Println(n)
}
func main(){
	for i := 0; i < 5; i++{
		wg.Add(1)
		go f(i)
	}
	wg.Wait()
}

3.互斥锁


var total int
var wg sync.WaitGroup
func add(){
	defer wg.Done()
	for i := 0; i < 5; i++{
		total = total+1
	}
}
func sub(){
	defer wg.Done()
	for i := 0; i < 5; i++{
		total = total-1
	}
}
func main(){
	wg.Add(2)
	
	go add()
	go sub()
	
	wg.Wait()
	fmt.Println(total)
}

理论结果:0
实际结果:不是0,随机-5~5,每次结果不一样

total = total+1

这个代码:
1.从total取出值
2.将total+1
3.将total+1的计算结果放回total中


var total int
var wg sync.WaitGroup
var lock sync.Mutex
func add(){
	defer wg.Done()
	for i := 0; i < 5; i++{
		lock.Lock()
		total = total+1
		lock.Unlock
	}
}
func sub(){
	defer wg.Done()
	for i := 0; i < 5; i++{
		lock.Lock()
		total = total-1
		lock.Unlock
	}
}
func main(){
	wg.Add(2)
	
	go add()
	go sub()
	
	wg.Wait()
	fmt.Println(total)
}

运行结果:0

4. 读写锁

互斥锁性能太差
绝大多数web系统都是读多写少


var total int
var wg sync.WaitGroup
var rwlock sync.RWMutex
func read(){
	defer wg.Done()
	rwlock.RLock()
	fmt.Println("开始读取数据")
	time.Sleep(time.Second*10)
	fmt.Println("读取成功")
	rwLock.RUlock()

}
func write(){
	defer wg.Done()
	rwlock.WLock()
	fmt.Println("开始修改数据")
	time.Sleep(time.Second*10)
	fmt.Println("修改成功")
	rwLock.WUlock()
}
func main(){
	wg.Add(1)
	for i := 0; i < 5; i++{
		wg.Add(1)
		go read()
	}
	go write()
	wg.Wait()
	fmt.Println(total)
}

读取之间互不影响,写锁进来都停止

5. channel

channel提供了一种通信机制,类似java的消息队列
关键字chan

func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	msg = make(chan int) //第一种初始化方式;无缓冲
	//msg = make(chan int,1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	data := <- msg  //将箭头右边的值放到左边
	fmt.Println(data)
}

运行结果:失败 deadlock
原因:无缓冲空间

func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	//msg <- 2 //再放就会报错,超出缓冲大小
	data := <- msg  //将箭头右边的值放到左边
	fmt.Println(data) 
}

运行结果:1


var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	data := <-queue 
	fmt.Println(data)
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	wg.Add(1)
	go consumer(msg)
	msg <- 2
	wg.Wait()
	
}

6. 遍历channel和关闭channel


var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	for data := range queue{
		fmt.Println(data)
		time.Sleep(time.Second)
	}
	
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	wg.Add(1)
	go consumer(msg)
	msg <- 2
	//关闭channel  1.已经关闭的channel不能再发送数据了 
	// 2.已经关闭的channel消费者可以继续取出数据
	close(msg)
	wg.Wait()
	
}

结果:
1
2


var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	for {
		data := <-queue 
		fmt.Println(data)
		time.Sleep(time.Second)
	}
	
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	wg.Add(1)
	go consumer(msg)
	msg <- 2
	//关闭channel  1.已经关闭的channel不能再发送数据了 
	// 2.已经关闭的channel消费者可以继续取出数据
	close(msg)
	wg.Wait()
	
}

结果:
1
2
0
0
0
0

chan取出的值有两个返回值,第二个是ok

var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	for {
		data,ok := <-queue 
		fmt.Println(data,ok)
		time.Sleep(time.Second)
	}
	
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	wg.Add(1)
	go consumer(msg)
	msg <- 2
	//关闭channel  1.已经关闭的channel不能再发送数据了 
	// 2.已经关闭的channel消费者可以继续取出数据
	close(msg)
	wg.Wait()
	
}

结果:
1 true
2 true
0 false
0 false
0 false
0 false

使用ok判读break时机


var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	for {
		data,ok := <-queue 
		if !ok{
			break
		}
		fmt.Println(data)
		time.Sleep(time.Second)
	}
	
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	//msg = make(chan int) //第一种初始化方式;无缓冲
	msg = make(chan int1) //第二种初始化方式;有缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	msg <- 1 //将1 放入channel中
	wg.Add(1)
	go consumer(msg)
	msg <- 2
	//关闭channel  1.已经关闭的channel不能再发送数据了 
	// 2.已经关闭的channel消费者可以继续取出数据
	close(msg)
	wg.Wait()
	
}

结果:
1
2

7.双向和单向channel

//无缓冲


var wg sync.WaitGroup
func consumer (queue chan int) {
	defer wg.Done()
	for {
		data,ok := <-queue 
		if !ok{
			break
		}
		fmt.Println(data)
		time.Sleep(time.Second)
	}
	
}
func main(){
	//1.定义一个channel
	var msg chan int
	//2.初始化这个channel,两种方式
	msg = make(chan int) //第一种初始化方式;无缓冲
	//在go语言中使用make初始化的有三种:1.slice 2.map 3.chan
	wg.Add(1)
	go consumer(msg)//无缓冲,先启动消费
	msg <- 1 //将1 放入channel中
	//msg <- 2
	//关闭channel  1.已经关闭的channel不能再发送数据了 
	// 2.已经关闭的channel消费者可以继续取出数据
	 close(msg)
	wg.Wait()
	
}

//单、双向


var wg sync.WaitGroup
func consumer (queue <-chan int) { //只能取值。单向
	defer wg.Done()
	for {
		data,ok := <-queue 
		if !ok{
			break
		}
		fmt.Println(data)
		time.Sleep(time.Second)=
	}
	
}
func main(){
 	var msg chan int //双向
	//var msg chan<- int //单向
	msg = make(chan int) 
	wg.Add(1)
	go consumer(msg)//普通的channel可以直接转换成单向的
	msg <- 1 //将1 放入channel中
	 close(msg)
	wg.Wait()
	
}

var wg sync.WaitGroup
func consumer (queue <-chan int) { //只能取值。单向
	defer wg.Done()
	for {
		data,ok := <-queue 
		if !ok{
			break
		}
		fmt.Println(data)
		time.Sleep(time.Second)=
	}
	
}
func stock(queue chan<- int{
	defer wg.Done
	for{
		queue <- 1
		time.Sleep(time.Second)
	}
}
func main(){
 	var msg chan int //双向
	//var msg chan<- int //单向
	msg = make(chan int) 
	wg.Add(1)
	go consumer(msg)//普通的channel可以直接转换成单向的
	msg <- 1 //将1 放入channel中
	 close(msg)
	wg.Wait()
	
}

deadlock出现场景

1.无缓冲直接放值,且没有一个goruntine去消费
2.channel是多个goruntine之间线程安全:1.获取锁,2.等到消费者消费,3.释放锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值