5. 并发的学习

Go语言并发编程详解

5.1 并发基础

5.1.1 并发和并行

5.1.2 goroutine

1) go的执行是非阻塞的,不会等待。

2) go后面函数的返回值会被忽略

3)调度器不能保证多个 goroutine 的执行次序

4) 没有父子goroutine的概念,所有的goroutine是平等地被调用和执行的。

5) Go程序在执行时会单独为 main 函数创建一个 gouroutine , 遇到其他go关键字时再去创建其他的goroutine

6) Go 没有暴露goroutine id 给用户,所以不能在一个goroutine里面显式地操作另一个goroutine,不过 runtime包提供了一些函数访问和设置goroutine的相关信息

1. func GOMAXPROCS

func GOMAXPROCS(n int)  int 用来设置或查询可以并发执行的goroutine数目,n 大于 1表示设置gomaxprocs值,否则表示查询gomaxprocs值
 

import "runtime"

func main() {

    // 获取当前GOMAXPROCS值

    println("GOMAXPROCS=", runtime.GOMAXPROCS(0))

    // 设置GOMAXPROCS值

    runtime.GOMAXPROCS(2)

    // 获取当前的GOMAXPROCS值

    println("GOMAXPROCS=", runtime.GOMAXPROCS(0))

}

2. func Goexit

用于结束当前goroutine

3. func Gosched

放弃当前调度执行机会

5.1.3 chan

Go的哲学,不要通过共享内存来通信 ,而是通过通信来共享内存。chan通道是Go通过通信来共享内存的载体。

通道是有类型的: chan dataType

//创建一个无缓冲的通道,通道存放元素的类型为dataType

make(chan datatype)

//创建一个有10个缓存的通道,通道存放元素的类型为datatype

通道氛围无缓冲的通道和有缓冲的通道,Go提供内置函数len和cap,

无缓冲的通过的len和cap都是0,

有缓冲的通过 len 代表没有背读取的元素数,cap代表整个通道的容量

无缓存的通道既可以用于通信,也可以用于两个goroutine的同步,有缓冲的通道主要用于通信

package main

import(
	"runtime"
)

func main() {
	c := make(chan struct {})
	go func(i chan struct {}) {
		sum := 0
		for i := 0; i < 10000; i++ {
			sum += i
		}
		println(sum)
		c <- struct {} {}
	}(c)
	// NumGoroutine可以返回当前程序的goroutine数目
	println("NumGoroutine =", runtime.NumGoroutine())

	//读通道c,通过通道就进行同步等待
	<-c
}

缓冲通道和消息队列类似,有削峰和增大吞吐量的功能,例如:

package main

import (
	"runtime"
)
func main() {
	c  := make(chan struct {})
	ci := make(chan int, 1000000)
    go func(i chan struct {}, j chan int) {
		for i := 0; i < 1000000; i++ {
			ci <- i
		}
		close(ci)
		//写通道
		c <- struct {} {}
	}(c, ci)

	// NumGoroutine 可以返回当前程序的goroutine数目
	println("NumGoroutine =", runtime.NumGoroutine())

	//读通道c, 通过通道进行同步等待
	<-c

	//此时ci通道已经关闭,匿名函数启动的goroutine已退出
	println("NumGoroutine =", runtime.NumGoroutine())

	//但通道ci还可以继续读取
	for v := range ci {
		println(v)
	}
}

panic

1) 向已经关闭的通道写数据会导致panic

最佳实践是由写入者关闭通道,能最大程度地避免向已经关闭的通道写数据而导致的panic

2)重复关闭的通道会导致panic

阻塞

1) 向未初始化的通道写数据或者读数据都会导致当前goroutine的永久阻塞

2)向缓存区已满的通道写入数据会导致 goroutine 阻塞。

3) 通道中没有数据,读取该通道会导致 goroutine 阻塞

非阻塞

1) 读取已经关闭的通道不会引发阻塞,而是立即返回通道元素类型的零值,可以使用 comma , ok 语法判断通道是否关闭

2)向有缓冲且没有满的通道读/写不会引发阻塞

5.1.4 WaitGroup

type WaitGroup struct {

    // contains filtered or unexported fields

}

 

// 添加等待信号

func (wg *WaitGroup) Add (delta int)

// 释放等待信号

func (wg *WaitGroup) Done()

//等待

func (wg *WaitGroup) Wait()

WaitGroup用来等待多个goroutine完成,main goroutine调用Add设置需要等待goroutine的数目,每一个goroutine结束时调用Done(),Wait()被main用来等待所有的goroutine完成

下面程序演示如何使用sync.WaitGroup完成多个goroutine之间的协同工作。

package main

import (
	"net/http"
	"sync"
)

var wg sync.WaitGroup
var urls = []string {
	"https://www.baidu.com/",
	"https://www.163.com/",
	"https://www.qq.com",
}

func main() {
	for _, url := range urls {

		// 每一个url启动一个goroutine,同时给wg加1
		wg.Add(1)

		// Launch a goroutine to fetch the URL.
		go func(url string) {
			//当前goroutine结束后给wg计数减1, wg.Done()等价于wg.Add(-1)
			// defer wq.Add(-1)
			defer wg.Done()

			//发送HTTP get请求并打印HTTP返回码
			resp, err := http.Get(url)
			if err == nil {
				println(resp.Status)
			}
		}(url)
	}
	//等待所有请求结束
	wg.Wait()
}

5.1.5 select

select 是类UNIX系统提供的一个多路复用系统API,Go语言借用多路复用的概念,提供了select关键字,用于多路监听多个通道。

当监听的通道没有状态是可读可写的,select是阻塞的;

只要监听的通道中有一个状态是可读或可写的,则 select 就不会阻塞,而是进入处理就绪通道的分支流程。

如果监听的通道有多个可读或可写的状态,则 select 随机选取一个处理。例如:

package main

func main() {
	ch := make(chan int, 1)
	go func(chan int) {
		for {
			select {
				//0或1的写入是随机的
			case ch <- 0:
			case ch <- 1:
			}
		}
	}(ch)
	for i := 0; i < 10; i++ {
		println(<-ch)
	}
}

5.1.6 扇入(Fan in) 和 扇出 (Fan out)

扇入指将多路通道聚合到一条通道中处理,Go语言最简单的扇入就是使用select聚合多条通道服务;

扇出指将一条通道伐散到多条通道中处理,Go语言使用 go 关键字启动多个 goroutine 并发处理。

5.1.7 通知退出机制

读取已经关闭的通道不会引发阻塞,也不会导致panic,而是立即返回该通道存储类型的零值。

关闭select监听的某个通道能使select立刻感知这种通知,然后进行处理,这就是所谓的退出通知机制

5.2 并发范式

5.2.1 生成器

159

5.2.2 管道

通道可以分为两个方向,一个是读,一个是写

5.2.3 每个请求一个goroutine

5.2.4 固定 worker 工作池

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值