Go语言为并发编程而内置的上层API基于CSP(communication sequential processes,顺序通信进程)模型。这就意味着显式锁都是可以避免的,比如资源竞争,比如多个进程同时获取文件资源需要修改,首先拿到资源的进程加上锁,等修改完之后把锁去掉,然后再给下一个进程来进行修改,只有这样才不会出现数据不一致。但是go语言不是通过锁的方式,是通过通信的方式,安全的通道发送和接收数据以实现同步,这就大大的简化了并发编程的编写。
一般情况下,一个普通的计算机跑十几二十几个线程就有点负载过大,但是同样这台机器却可以让成千甚至过万哥goroutine进行资源竞争
goroutine是什么?
goroutine是Go并行设计的核心。goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需要极少的栈内存(大概是4-5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
创建goroutine
创建goroutine只需在函数调用语句前添加go关键字,就可以创建并发执行单元。开发人员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上执行。
在并发编程里,我们通常想将一个过程切分成几块,然后让每个goroutine各自负责一块工作。当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine,新的goroutine会用go语句来创建,至于主和子谁先执行,由系统决定。重点:主协程退出了,其它子协程也要跟着退出;其次,主协程先退出导致子协程没来得及调用
package main
import (
"fmt"
"time"
)
func newTask() {
for {
fmt.Println("this is a newTask")
time.Sleep(time.Second) //延时1s
}
}
func main() { //可以理解为主 goroutine
go newTask() //新建一个协程, 新建一个任务
for {
fmt.Println("this is a main goroutine")
time.Sleep(time.Second) //延时1s
}
//newTask() 子协程(goroutine)如果放在这会执行不到,因为程序都是单业务。for循环是一个任务,下面执行不到
//go newTask() 但是前面加上go关键字,放到下面也没法执行
}
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask
主协程退出了,其它子协程也要跟着退出。
package main
import (
"fmt"
"time"
)
//主协程退出了,其它子协程也要跟着退出
func main() {
go func() {
i := 0
for {
i++
fmt.Println("子协程 i = ", i)
time.Sleep(time.Second)
}
}()
i := 0
for {
i++
fmt.Println("main i = ", i)
time.Sleep(time.Second)
if i == 2 {
break
}
}
}
main i = 1
子协程 i = 1
main i = 2
子协程 i = 2
主协程先退出导致子协程没来得及调用
package main
import (
"fmt"
"time"
)
//主协程退出了,其它子协程也要跟着退出
func main() {
go func() {
i := 0
for {
i++
fmt.Println("子协程 i = ", i)
time.Sleep(time.Second)
}
}()
}
//执行代码并无任何输出,因为主协程先执行完退出