他来了,他来了,go语言最迷人的地方。
就是go语言的go程吸引我学习了go语言,相信他可以陪伴我一声。
goroutine
简介
Golang中最迷人的一个优点就是语言底层支持并发。
在Golang中的goroutine(协程)类似于其他语言的线程。
并发和并行
- 并行(parallelism)指不同的代码片段同时在不同的物理处理器上支持。
- 并发(concurrency)指同时管理多个事情,物理处理器上可以运行某个内容一半后就处理其他事情。
- 在一般看来并发的性能要好于并行,因为计算机的物力资源是固定的,较少的,而程序需要执行的内容是很多的,所以并发是”比较少的资源去去做更多事情。”
几种主流并发模型
- 多线程,每个线程只处理一个请求,只有请求结束后,对应的线程才会接受下一个请求,这种模式在并发下,性能开销极大。
- 基于回调的异步IO,在程序运行过程中可能产生大量回调导致维护成本加大,程序执行流程也不便于四维。
- 协程,不需要抢占调试,可以有效提升线程任务的并发性,弥补了多线程模式的缺点;Golang在语言层面就支持,而其他语言很少支持
goroutine的语法。
- 表达式可以是一条语句。
- 表达式可以是函数,函数返回值即有,也无效,当函数执行完成此goroutine自动结束。
1
go 表达式
代码示例
对比多次调用函数和使用goroutine的效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import "fmt"
func main() {
//正常调用,输出三遍 1 2 3 4 5 (每个数字后换行)
//for i := 1;i<=3;i++ {
// go demo()
//}
/*
添加go关键字后发现控制台什么也没有输出
原因:把demo() 设置到协程后没得到函数执行,主线程执行结束
*/
for i := 1; i <= 3; i++ {
go demo(i)
}
}
func demo(index int) {
for i := 1; i <= 5; i++ {
fmt.Printf("第%d执行,i的值为%d\n", index, i)
}
}添加休眠等待goroutine执行结束。
这种方式很大的问题就是休眠时间,如果休眠时间设置过小,可能goroutine并没有执行完成,如果休眠时间设置过大,影响结束程序执行。找到的本次执行的休眠时间,下次程序执行时这个休眠时间可能”过大”或“过小“
通过程序运行结果发现每次执行结果都不一定是一样的,因为每个demo() 都是并发执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package main
import (
"fmt"
"time"
)
func main() {
//正常调用,输出三遍 1 2 3 4 5 (每个数字后换行)
//for i := 1;i<=3;i++ {
// go demo()
//}
/*
添加go关键字后发现控制台什么也没有输出
原因:把demo() 设置到协程后没得到函数执行,主线程执行结束
*/
for i := 1; i <= 3; i++ {
go demo(i)
time.Sleep(3e9)
}
}
func demo(index int) {
for i := 1; i <= 5; i++ {
fmt.Printf("第%d执行,i的值为%d\n", index, i)
}
}
WaitGroup
简介
Golang中sync包提供了基本同步基元,如互斥锁等,除了Once和WaitGroup类型,大部分都只适用于低水平程序线程,高水平同步线程使用channel通信更好一些。
WaitGroup直译为等待组,其实就是一个计数器,只要计数器中有内容将一直阻塞。
在Golang中WaitGroup存在于sync包中,在sync包中类型都是不应被拷贝的,源码定义如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of
// goroutines to wait for. Then each of the goroutines
// runs and calls Done when finished. At the same time,
// Wait can be used to block until all goroutines have finished.
//
// A WaitGroup must not be copied after first use.
type WaitGroup struct {
noCopy noCopy
// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
// 64-bit atomic operations require 64-bit alignment, but 32-bit
// compilers do not ensure it. So we allocate 12 bytes and then use
// the aligned 8 bytes in them as state.
state1 [12]byte
sema uint32
}Go语言标准库中WaitGroup只有三个方法。
- add(delta int)表示向内部计数器添加增量(delta),其中参数delta可以是负数。
- Done()表示减少WaitGroup计数器的值,应当在程序最后执行。相当于Add(-1)
- Wait() 表示阻塞知道WaitGroup计数器为0
1
2
3
4type WaitGroup
func (wg *WaitGroup) add(delta int)
func (wg *WaitGroup) Done()
func (Wg *WaitGroup) Wait()
代码示例
- 使用WaitGroup可以有效解决goroutine未执行完成主协程执行完成,导致程序结束,goroutine未执行问题
1 | package main |