go语言并发编程与原理

一、前置知识

1、CPU、操作系统与多线程

1.1单进程时代的特点和问题

在这里插入图片描述

单进程时代的两个问题:
① 单一执行流程、计算机只能一个任务一个任务地处理。
② 进程阻塞所带来的CPU浪费时间

1.2多线程、多进程的特点和问题

在这里插入图片描述

多进程、多线程解决了阻塞问题。
多线程带来新的问题:

①线程的切换是需要时间和计算成本的,因为需要保存未完成任务的线程的状态。如图。
在这里插入图片描述

进程、线程的数量越多,切换成本就越大,也就越浪费。
CPU可能60%执行程序,40%在切换中。

②多线程带来的开发设计难度增大,越来越复杂:同步竞争(如锁、共享内存、竞争资源冲突等)。

③高内存占用。如图。
在这里插入图片描述

解决上述问题,就是下一代语言应考虑的问题。

2、协程

在这里插入图片描述

一个线程,操作系统分为用户空间(用户态)和内核空间(内核态)。内核态表示操作系统底层,包括进程的开辟啊,分配物理内存资源啊,分配磁盘资源等等。用户空间就是上层开发写业务逻辑的调接口的。我们能不能把这样的线程一分为二呢?给他划分为一个内核线程和一个用户线程呢?

如果这样切换成功的话,我们把用户线程和内核线程是做了一个绑定。这个内核线程单独用于处理硬件问题,用户线程用来保证业务层面并发的效果。CPU是硬件,不管你是线程还是进程,它的视野就只能看到内核,CPU本身是无感的,操作系统不需要变。操作系统本身不知道的。所以说这么划分完之后,操作系统不需要改代码。

内核线程就是thread。
用户线程就起个别名叫协程co-routine。

他们是一一绑定的。

在这里插入图片描述

调度器管理线程、协程有三种关系模式:

在这里插入图片描述
N:1关系解决了线程切换的高CPU消耗的切换成本问题,但阻塞问题无法解决。
在这里插入图片描述

1:1关系完全退化为了原来的多线程模型,还是会存在高切换成本问题。

在这里插入图片描述
不同语言做不同的协程调度器,优化得越好效率越高。

二、Golang中的协程

1、Golang对协程的处理

在这里插入图片描述
灵活调度体现在调度器

2、Golang早期调度器

Golang早期的调度器是比较差的。
当M0想获取一个协程,首先从全局队列中,尝试获取锁,取一个G过来执行,执行完尝试还锁,放回到队列。
在这里插入图片描述
这种调度器非常简单,有很多弊端:
在这里插入图片描述

3、Golang优化后的调度器

3.1、GMP模型

在这里插入图片描述
在这里插入图片描述

3.2、调度器设计策略

1、复用线程
work stealing机制

在这里插入图片描述

hand off机制

在这里插入图片描述

2、利用并行
在这里插入图片描述

3、抢占

在这里插入图片描述

4、全局G队列
在这里插入图片描述

三、goroutine实践

1、goroutine的开启与关闭

package main
import (
	"fmt"
	"runtime"
	"time"
)
func main() {
		//用关键字go创建承载一个形参为空,返回值为空的一个函数
		go func() {
			defer fmt.Println("A.defer就算Goexit()退出线程也会执行。")
			func() {
				defer fmt.Println("B.defer就算Goexit()退出线程也会执行。")
				//退出当前goroutine,注掉放开看效果
				runtime.Goexit()
				fmt.Println("B若Goexit()退出当前线程,则后面的此行代码不会执行。")
			}()
			fmt.Println("A若Goexit()退出当前线程,则后面的此行代码不会执行。")
		}()
	go func(a int, b int) bool {
		fmt.Println("a = ", a, ", b = ", b)
		return true//这里的return是不会被接收的,引出goroutine之间传值需要用到的channel
	}(10, 20)
	time.Sleep(3 * time.Second)//主goroutine等待让子goroutine执行完毕。
}

2、无缓冲的channel

package main
import "fmt"
func main() {
	//定义一个无缓冲channel
	c := make(chan int)
	go func() {
		defer fmt.Println("goroutine结束")
		fmt.Println("goroutine 正在运行...")
		c <- 666 //将666 发送给c
	}()
	num := <-c //从c中接受数据,并赋值给num
	fmt.Println("num = ", num)
	fmt.Println("main goroutine 结束...")
}

3、有缓冲的channel

package main
import (
	"fmt"
	"time"
)
func main() {
	c := make(chan int, 3) //带有缓冲的channel
	fmt.Println("len(c) = ", len(c), ", cap(c)", cap(c))
	go func() {
		defer fmt.Println("子go程结束")
		for i := 0; i < 5; i++ {//发送的比接收的多一个元素,最终在channel中未被使用,但不会阻塞两边的线程。
			c <- i
			fmt.Println("子go程正在运行, 发送的元素=", i, " len(c)=", len(c), ", cap(c)=", cap(c))
		}
	}()
	fmt.Println("主线程开始延时等待,发送者阻塞")
	time.Sleep(2 * time.Second)
	fmt.Println("主线程结束延时等待,开始接收")
	for i := 0; i < 4; i++ {
		num := <-c //从c中接收数据,并赋值给num
		fmt.Println("num = ", num)
	}
	time.Sleep( time.Second/2)
	fmt.Println("main 结束")
}

4、channel的关闭

package main
import "fmt"
func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		//close可以关闭一个channel
		close(c)
	}()
	for {//如果不关闭channel,这里会形成死锁。
		//ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}
	fmt.Println("Main Finished..")
}

5、channel与for-range

上述代码可以用for-range进行简写:

package main
import "fmt"
func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		//close可以关闭一个channel
		close(c)
	}()
	//可以使用range来迭代不断操作channel
	for data := range c {
		fmt.Println(data)
	}
	fmt.Println("Main Finished..")
}

6、channel与select-case

package main
import "fmt"
func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {//如果没有selectcase,同一时刻只能监听一个channel
		select {
		case c <- x://如果c可写,则该case就会进来。
			x = y
			y = x + y
		case <-quit://如果quit可读,则该case就会进来。
			fmt.Println("quit")
			return
		}
	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)
	//sub go
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	//main go
	fibonacii(c, quit)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值