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,这样只会有一个是有效的

相关推荐
2401_857622661 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589361 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没3 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码4 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries5 小时前
读《show your work》的一点感悟
后端
A尘埃5 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23075 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code5 小时前
(Django)初步使用
后端·python·django
代码之光_19805 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端