Go语言 您所在的位置:网站首页 go语言高并发 Go语言

Go语言

2023-11-12 07:18| 来源: 网络整理| 查看: 265

目录

1、基本概念

2、sync.WaitGroup

3、goroutine和线程

4、channel

5、无缓冲通道和缓冲通道

6、生产者和消费者模型

7、select 多路复用

8、单向通道

总结

1、基本概念

并发:是指一个时间段中几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,担任一个时刻点上只有一个程序在处理机上运行。同一时间段同时在做多个事情。

并行:在操作系统中指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。同一时刻同时在做多个事情。

进程:一个程序在启动之后创建一个进程。

线程:操作系统调度的最小单元。

协程:用户态的线程。

goroutine:go关键字为一个函数创建一个goroutine。一个函数可以被创建多个goroutine,一个goroutine必定对应一个函数。

实例:等待goroutine运行完main在结束。

package main import ( "fmt" "time" ) func hello() { fmt.Println("hello函数") } func main() { go hello() //先创建一个goroutine,再goroutine中执行hello函数 //等待1秒钟,再执行main函数 time.Sleep(time.Second) fmt.Println("main函数") } //运行结果为: hello函数 main函数

实例:加入defer语句。

package main import ( "fmt" "time" ) func hello() { fmt.Println("hello函数") } func main() { //在程序执行完之前执行 defer fmt.Println("defer函数") go hello() //先创建一个goroutine,再goroutine中执行hello函数 //等待1秒钟,再执行main函数 time.Sleep(time.Second) //再执行main函数 fmt.Println("main函数") } //运行结果为: hello函数 main函数 defer函数 2、sync.WaitGroup

使用sync.WaitGroup无需设置等待时间,它会自动等待所有goroutine执行完成后在结束main,效率提升。

实例:

package main import ( "fmt" "sync" ) //计数器结构体实例 var sw sync.WaitGroup func hello() { fmt.Println("hello函数") //告知计数器运行完毕,次数-1 sw.Done() } func test() { fmt.Println("test函数") sw.Done() } func main() { defer fmt.Println("defer语句") //设置计数器次数 sw.Add(2) go hello() go test() fmt.Println("main函数") //等待计数器归零,结束main。否则一直处于等待状态,不关闭main函数。 sw.Wait() } //运行结果为: main函数 hello函数 test函数 defer语句

实例:启动10个goroutine执行。

package main import ( "fmt" "sync" ) //启用10个goroutine var sw sync.WaitGroup func hello(i int) { fmt.Println("hello函数", i) sw.Done() } func main() { sw.Add(10) //创建10个goroutine for i := 0; i < 10; i++ { go hello(i) } fmt.Println("main函数") sw.Wait() } //运行结果为: hello函数 4 main函数 hello函数 6 hello函数 0 hello函数 2 hello函数 3 hello函数 9 hello函数 8 hello函数 7 hello函数 1 hello函数 5

示例:panic宕机前把错误信息发送到控制台上,程序结束,资源全部释放。

package main import ( "fmt" "sync" ) //定义sync.WaitGroup结构体,内置计数器 var sw sync.WaitGroup func hello(i int) { //计数器-1,goroutine会全部执行完成 fmt.Println("hello函数", i) if i == 6 { panic("宕机报警") } //遇到panic不执行 sw.Done() } func main() { defer fmt.Println("defer语句") //启用10个goroutine sw.Add(10) for i := 0; i < 10; i++ { go hello(i) } sw.Wait() } //运行结果为: hello函数 9 hello函数 5 hello函数 6 hello函数 4 hello函数 1 panic: 宕机报警 3、goroutine和线程

可增长的栈:OS线程(操作系统线程)一般都有固定的栈内存(2M),一个goroutine的栈在生命周期开始时只有很小的栈(2K),goroutine的栈是不固定的可以按需增大或缩小,goroutine的栈大小限制可达到1G,虽然这种情况不多见,所以一次性创建十万左右的goroutine是没问题的。

goroutine调度:OS线程由OS内核来调度,goroutine则是由Go运行时(runtime)自己的调度器来调度,这个调度器使用一个m:n调度的技术(复用/调度m个goroutine到n个OS线程),goroutine的调度不需要切换内核语境,所以调用一个goroutine比调用个线程的成本要低很多。

GOMAXPROCS:Go运行时的调度使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码,默认值是机器上的CPU核心数。例如:在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m.n调度中的n)。

Go可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。(Go1.5版本前默认是单核心执行,Go1.5版本后默认使用全部逻辑核心数)。

示例:通过将任务分配到不同的CPU逻辑核心上实现并行效果。

package main import ( "fmt" "runtime" "sync" ) var sw sync.WaitGroup func a() { defer sw.Done() for i := 0; i < 5; i++ { fmt.Println("A:", i) } } func b() { defer sw.Done() for i := 0; i < 5; i++ { fmt.Println("B:", i) } } func main() { //使用两个逻辑核心数跑go程序 runtime.GOMAXPROCS(2) sw.Add(2) go a() go b() sw.Wait() } //运行结果为: B: 0 A: 0 A: 1 A: 2 A: 3 A: 4 B: 1 B: 2 B: 3 B: 4 4、channel

单纯的将函数并发执行没有意义。函数与函数间需要交换数据才能体现并发执行函数的意义。

虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题。

go语言的并发模型是SCP,提倡通过通信共享内存而不是通过共享内存而实现通信。

如果说goroutine是Go程序并发的执行体,channel就是它们之间连接。channel是可以让一个goroutine发送特定值到另个goroutine的通信机制。

go语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,在声明channel的时候需要为其指定元素类型。

声明语法:

var 变量 chan 元素类型 var ch1 chan int //传递整型的通道 var ch2 chan bool //传递布尔型的通道 var ch3 chan []int //传递整型切片的通道

示例:使用channel传递数据。

package main import "fmt" //channel定义 func main() { //通道ch1传输int元素数据 var ch1 chan int //通道ch2传输布尔元素数据 var ch2 chan bool fmt.Println("ch1", ch1) fmt.Println("ch2", ch2) //使用make初始化,需要定义传入的值的个数 ch3 := make(chan int, 2) //发送 ch3


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有