1、并发的含义
并发与并行的区别:
并发:逻辑上具备同时处理多任务的能力(由程序的逻辑实现决定)
并行:物理上在同一时刻执行多个并发任务(由的处理器核数决定)
实际的单核处理器在处理任务的时候基本都是以间隔方式切换执行(时间片调度),并行是并发程序在设计的理想执行模式。
任务可以并行执行其中多线程和多进程是基本条件。
然而在单线程中引入了一个比线程还轻量级的运行单元,协程(coroutine)实现并发。
如果处理器为单核单线程,虽然一次只可以处理一个任务,但是并发的程序设计(使用协程)不但可以减少多线程带来的资源开销,也可以减少阻塞的时间占比,提高执行效率
(协程在内部执行时是串行,并且可以控制调度(程序内部实现),不需要进行同步处理,提高了并发安全)
实现并发的三种程序实现方式:
多进程:主要用于分布式,负载均衡,减轻单进程垃圾回收压力(开销大,程序健壮,切换麻烦)
多线程:抢占更多的cpu资源,(开销相对较小,并发量大于进程)
多协程:提高cpu时间片利用率(开销更小,并发量更大)
更多的时候程序是使用这三种方式组合的方式实现的。
2、Golang中的goroutine
Goroutine类似于coroutine但是又区别于coroutine,因为在go语言中在执行一个程序时会先创建多个线程用于执行并发任务,并且又在多线程的基础上进行多goroutine的并发设计,用于提高程序的执行效率
goroutine更像线程和协程的结合体
golang中在函数的调用前加上关键字go即可创建并发任务,新建的任务会被添加到任务队列中而不是立即去执行,当前流程不会被阻塞。
在程序中每个任务单元除了会保存函数指针,调用参数外,系统都会分配栈内存空间。系统默认以MB为单位,goroutine自定义的初始栈仅为2kb,因此golang有利于千万的并发。
在golang中goroutine,sync.WaitGroup,runtime.GOMAXPROCS的联合使用
package main
import (
"fmt"
"math"
"runtime"
"sync"
)
func count(){
x := 0;
for i:=0;i<math.MaxUint32;i++{
x +=i
}
fmt.Println(x)
}
func test(n int){
for i:=0;i<n;i++{
count()
}
}
func test2(n int){
var wg sync.WaitGroup
wg.Add(n)
for i:=0;i<n;i++{
go func() {
count()
wg.Done()
}()
}
wg.Wait()
}
func main(){
//n := runtime.GOMAXPROCS(0)
m := runtime.GOMAXPROCS(runtime.NumCPU())
//test(n)
test2(m)
}
//非并发执行
//9223372030412324865
//9223372030412324865
//9223372030412324865
//9223372030412324865
//
//real 0m9.733s //程序实际执行时间
//user 0m9.745s //cpu执行时间(单核)
//sys 0m0.003s
//并发执行
//9223372030412324865
//9223372030412324865
//9223372030412324865
//9223372030412324865
//
//real 0m2.343s //程序实际执行时间
//user 0m8.309s //多核执行时间的累加
//sys 0m0.010s
与线程不同的是goroutine创建的任务单元无法设置优先级,并且无法获取编号,没有局部存储(TLS),甚至连返回值有时也会被抛弃。这些功能除了优先级,其他都可实现
package main
import (
"fmt"
"sync"
)
func main(){
var wg sync.WaitGroup
var gs [5]struct{ //用于实现TLS功能
id int //编号
result int //返回值
}
for i:=0;i<len(gs);i++{
wg.Add(1)
go func(id int){
defer wg.Done()
gs[id].id = id
gs[id].result = (id+1)*100
}(i)
}
wg.Wait()
fmt.Printf("%+v\n",gs)
}
//[{id:0 result:100} {id:1 result:200} {id:2 result:300} {id:3 result:400} {id:4 result:500}]
3、Gosched和Goexit
gosched主动让出时间片,让出时间片的时长为10ms
package main
import (
"fmt"
"runtime"
)
func main(){
runtime.GOMAXPROCS(1)
exit := make(chan struct{})
go func (){ //任务c
println("c")
}()
go func() { //任务a
defer close(exit)
go func(){ //任务b
fmt.Println("cd")
runtime.Gosched()
fmt.Println("ab")
}()
for i := 0; i < 4; i++{
println("a:",i)
if i == 1 {
runtime.Gosched() //让出时间片
}
}
}()
<-exit
}
//a: 0
//a: 1
//cd
//c
//a: 2
//a: 3
goexit终止当前任务,不会影响其他并发任务,不会引发panic,不会被捕获
package main
import "runtime"
func main(){
exit := make(chan struct{})
go func(){
defer close(exit)
defer println("a")
func(){
defer func() {
println("b",recover() == nil)//执行,recover返回nil
}()
func(){
println("c")
runtime.Goexit() //停止整个堆栈调用
println("c done") //以下不会执行
}()
println("b done")
}()
println("a done")
}()
go func() {
println("ddddd")
}()
<-exit
println("main exit")
}
//c
//b true
//a
//main exit
//ddddd
package main
import (
"fmt"
"runtime"
"time"
)
func teste(){
fmt.Println("bb")
runtime.Goexit()
fmt.Println("dd") //不会执行
}
func main(){
fmt.Println("aa")
go teste()
time.Sleep(10*time.Second)
fmt.Println("cc") //会执行
}
package main
import (
"fmt"
"runtime"
)
func teste(){
fmt.Println("bb")
//runtime.Goexit()
fmt.Println("dd") //不会执行
}
func main(){
fmt.Println("aa")
go teste()
runtime.Goexit() //会等待其他任务结束,然后进程崩溃
fmt.Println("cc")
}
//aa
//bb
//dd
//fatal error: no goroutines (main called runtime.Goexit) - deadlock!