olang中Goroutines与Channels应用解析

前言

并发指的是同时进行多个任务的程序,Web处理请求,读写处理操作,I/O操作都可以充分利用并发增长处理速度,随着网络的普及,并发操作逐渐不可或缺


一、Goroutines

1、Goroutine的定义

在Golang中一个Goroutines就是一个执行单元,而每个程序都应该有一个主函数main也就是主Goroutines,从某种意义上你也可以把goroutine当作一个线程。


2、Goroutine的使用

Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个 goroutine,从而让该函数或方法在新创建的 goroutine 中执行。 go func


3、Goroutine的特点

在主Goroutine结束之后其他的所有Goroutine都会直接退出。例如:

python 复制代码
import "fmt"

func main() {
	go getnum([]int{1, 5, 4, 84, 8, 4})
	fmt.Println("程序已经结束啦")

}
func getnum(s []int) {
	for _, v := range s {
		fmt.Println(v)
	}
}              
//程序已经结束啦

如果让主Gororutines睡一会

python 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	go getnum([]int{1, 5, 4, 84, 8, 4})
	time.Sleep(time.Second)
	fmt.Println("程序已经结束啦")

}
func getnum(s []int) {
	for _, v := range s {
		fmt.Print(v)
	}
}
//1548484程序已经结束啦   

差别显而易见


4、多个Goroutine的使用

要想使用多个Goroutine可以使用到waitgroup

go 复制代码
package main

import (
	"fmt"
	"sync"
)

// 声明全局等待组变量
var wg sync.WaitGroup

func worker(i int) {
	fmt.Printf("%d 开始working...\n", i)
	wg.Done() // 告知当前goroutine完成,释放资源
}

func main() {
	for i := 0; i <= 10; i++ {
		wg.Add(1) // 登记1个goroutine,开放一个进程供其使用
		go worker(i)
	}
	wg.Wait() // 阻塞等待登记的goroutine完成,不然主Go结束了就都退出了
	fmt.Println("程序结束了...")
/*
10 开始working...
2 开始working...
0 开始working...
4 开始working...
1 开始working...
7 开始working...
8 开始working...
5 开始working...
6 开始working...
9 开始working...
3 开始working...
程序结束了...  
 */
}

其中Add,wait,Done的作用在代码块中已经描述出来了。

那么如果想让每个working有序的打印那么就只需要将wg.Wait放在for循环当中就可以了 但是这样子的话仔细思考一下,它和串行还有什么区别呢?那么想要真正做到并发且有序打印应该怎么做? 在学习笔记之go语句的执行规则中我有学习过


二、Channels

1、介绍

Go语言中goroutine是程序的并发体,而channel则是它们这些并发体的通信工具,它可以让不同的goroutine之间发送信息


2、 channel的使用

channel跟map类似的在使用之前都需要使用make进行初始化 ch1 := make(chan int, 5)

未初始化的channel零值默认为nil

go 复制代码
var ch chan int
fmt.Println(ch) // <nil>

channel也拥有close方法,当channel使用结束后就可以close释放掉channel

channel缓冲区

看这个程序:

go 复制代码
package main

import "fmt"

func main() {
	ch1 := make(chan int)
	ch1 <- 5
	rec := <-ch1
	fmt.Println("ch1被接受,程序结束:rec:,", rec)
}
//fatal error: all goroutines are asleep - deadlock!          

由于ch1没有缓冲区,channel没有缓冲区的话:

只有在有接收方能够接收值的时候才能发送成功,否则会一直处于等待发送的阶段。同理,如果对一个无缓冲通道执行接收操作时,没有任何向通道中发送值的操作那么也会导致接收操作阻塞。

如果想要运行成功那么在发送信息前就应该有另外的进程等待着接收

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan int)
	go receive(ch1)
	ch1 <- 5
	time.Sleep(time.Second)

}
func receive(ch1 chan int) {
	for {
		select {
		case rec2 := <-ch1:
			fmt.Println("ch1被接受,程序结束:rec:,", rec2)
		}
	}
}
//ch1被接受,程序结束:rec:, 5  

但是如果有缓冲区就能避免程序阻塞,可以将发送的channel放在缓冲区直至有接收方将它接收

3、 单向通道

<- chan int // 只接收通道,只能接收不能发送 chan <- int // 只发送通道,只能发送不能接收 而对于close方法只能是发送通道拥有

4、select与channel配合使用

Select 的使用方式类似于 switch 语句,它也有一系列 case 分支和一个默认的分支。 每个 case分支会对应一个通道的通信(接收或发送)过程。select 会一直等待,直到其中的某个 case 的通信操作完成时,就会执行该 case分支对应的语句。

具体格式如下:

go 复制代码
select {
case <-ch1:
	//...
case rec := <-ch2:
	//...
case ch3 <- 10:
	//...
default:
	//默认操作
}

使用select的话

可处理一个或多个 channel 的发送/接收操作。 如果多个 case 同时满足,select 会随机选择一个执行。 对于没有 case 的 select 会一直阻塞,可用于阻塞 main 函数,防止退出

三、并发锁

1、互斥锁

使用

go 复制代码
var tex sync.Mutex
tex.Lock()
tex.Unlock()

当一个 goroutine 获取互斥锁之后,其他的 goroutine 将等待直至解锁。

2、读写锁

使用

go 复制代码
var tex sync.RWMutex
tex.Lock()
tex.Unlock()

读写锁分为两种:读锁和写锁。 当一个 goroutine 获取到读锁之后,其他的 goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;
而当一个 goroutine 获取写锁之后,其他的 goroutine无论是获取读锁还是写锁都会等待。


3、使用场景

如果并发过程中绝大部分都是读操作,那么使用读写锁将会优于互斥锁,如果读操作并不是特别多那将不会有太大差别


4、使用原因

多个 goroutine 同时操作一个资源(临界区)的情况,这种情况下就会发生竞态问题(数据竞态)

例如

go 复制代码
package main

import (
	"fmt"
	"sync"
)

var (
	x int64

	wg sync.WaitGroup // 等待组
)

// add 对全局变量x执行5000次加1操作
func add() {
	for i := 0; i < 5000; i++ {
		x = x + 1
	}
	wg.Done()
}

func main() {
	wg.Add(2)

	go add()
	go add()

	wg.Wait()
	fmt.Println(x)
}
//结果都是小于5000的数字

为什么结果不是5000呢,因为在并发的过程有可能会出现多个go同时执行x=x+1,这样只会有一个是有效的

相关推荐
追逐时光者2 小时前
免费、简单、直观的数据库设计工具和 SQL 生成器
后端·mysql
初晴~2 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581362 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳3 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾3 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
星就前端叭3 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
小林coding4 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者4 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu
从善若水5 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust