这是我参与「第五届青训营」伴学笔记创作活动的第2天
Goroutine
首先了解一下进程、线程、协程这三个的概念。
- 进程:简单来说用户运行程序时,就会产生进程,同一个程序会产生多个进程。
- 线程:有时被称为轻量级进程,是执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源。
- 协程:又称微线程与子例程(或者称为函数)一样,协程(coroutine)也是一种程序组件。
线程与协程区别
- 线程:用户态、轻量级线程、栈MB级别
- 协程:内核态、线程里可以跑多个协程,栈KB级别
func hello(i int) {
println("hello goroutine" + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) { //启动协程
hello(j)
}(i)
}
time.Sleep(time.Second) //主线程休眠,防止协程未执行,主线程结束。
}
go启动协程的方式就是使用关键字go,后面一般接一个函数或者匿名函数。
CSP
go 是通过通信共享内存,而不是通过共享内存而实现通信。
Channel(管道)
make(chan 元素类型,[缓冲区大小])
- 无缓冲管道:make(chan int)
- 有缓冲管道:make(cahn int 2)
func Cal() {
src := make(chan int) //无缓冲通道
dest := make(chan int ,3)//有缓冲通道
//相当于生产
go func() { //协程A发送0~9数字
defer close(src) //延迟资源关闭
for i :=0;i<9;i++{
src <- i
}
}()
//相当于消费
go func() { //协程B计算输入数字的平方
defer close(dest)//延迟资源关闭
for i :=range src{ //遍历src
dest <- i * i
}
}()
//主协程输出最后数的平方
for i := range dest{
println(i)
}
}
输出结果:
0 1 4 9 16 25 36 49 64
并发安全Lock
var x int64
var lock sync.Mutex
// 加锁的方法
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock() //加锁
x += 1
lock.Unlock() //释放锁
}
}
// 未加锁的方法
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func main() {
x = 0
//5 个协程并发
for i := 0; i < 5; i++ {
go addWithLock() //加锁
}
time.Sleep(time.Second) //休眠
println("addWithLock", x)
for i := 0; i < 5; i++ {
go addWithoutLock() //未加锁
}
time.Sleep(time.Second)//休眠
println("addWithoutLock", x)
}
addWithLock 10000; addWithoutLock 7919
WaitGroup
func HelloGoRoutine() {
var wg sync.WaitGroup
wg.Add(5) //计数器
for i := 0; i < 5; i++ {
go func(j int) { //启动协程
defer wg.Done() //计数器-1
hello(j)
}(i)
}
wg.Wait() //用于阻塞,直到计数器为0
}
测试
参考文章: