Golang的并发与goroutine

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!

猜你喜欢

转载自blog.csdn.net/alvin_666/article/details/84728643