go关于并发编程的操作
[*]多线程程序在一个核的CPU运行
https://cdn.nlark.com/yuque/0/2023/png/35902537/1685453577663-714d9c16-e8a3-4828-bb86-86dfa10c8e52.png#averageHue=%23f1f1f1&clientId=ue0ed4711-e7f1-4&from=paste&height=72&id=u67a9fbb6&originHeight=180&originWidth=978&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=19994&status=done&style=none&taskId=u5d1aaa37-1872-4f1b-af5f-ab7044ddf7a&title=&width=391.2
[*]多线程程序在多个核的CPU运行
https://cdn.nlark.com/yuque/0/2023/png/35902537/1685453587933-b30c5c52-ff07-4403-8c46-9ba8ac574b6d.png#averageHue=%23c7e2e7&clientId=ue0ed4711-e7f1-4&from=paste&height=74&id=ua1695715&originHeight=186&originWidth=978&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=20905&status=done&style=none&taskId=uc2b0c34f-018c-4e3f-8ae3-5a60c7a4006&title=&width=391.2
https://cdn.nlark.com/yuque/0/2023/png/35902537/1685453605197-22342698-262f-4a55-b4fc-90593a376b21.png#averageHue=%2387b683&clientId=ue0ed4711-e7f1-4&from=paste&height=183&id=u304fd657&originHeight=458&originWidth=1912&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=126713&status=done&style=none&taskId=ud88c6d4e-e668-4152-93bb-f12203df386&title=&width=764.8
goroutine
协程:用户态,轻量级线程,栈KB级别,创建和调度由go语言直接调度
线程:内核态,线程跑多个协程,栈MB级别
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine: " + fmt.Sprint(i))
}
func hello_goroutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}https://cdn.nlark.com/yuque/0/2023/png/35902537/1685500893642-9dfab6f2-8d1c-4c0c-87bf-e4820b681448.png#averageHue=%23303030&clientId=ue0ed4711-e7f1-4&from=paste&height=213&id=ueb5800cb&originHeight=532&originWidth=1224&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=67019&status=done&style=none&taskId=u5f6259ec-1496-4030-aff4-0fb2c5120c5&title=&width=489.6
go关键字直接可以开启新的协程
CSP模型
https://cdn.nlark.com/yuque/0/2023/png/35902537/1685501084549-6a2f13df-ad45-45eb-8d67-664e91a6be1e.png#averageHue=%23f6f6f6&clientId=ue0ed4711-e7f1-4&from=paste&height=376&id=u31ed0317&originHeight=940&originWidth=1486&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=178764&status=done&style=none&taskId=u93fa3889-50d4-494c-bed5-bcfdd4f79ff&title=&width=594.4
CSP 是 Communicating Sequential Process 的简称,中文可以叫做通信顺序进程,是一种并发编程模型,是一个很强大的并发数据模型,是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。相对于Actor模型,CSP中channel是第一类对象,它不关注发送消息的实体,而关注与发送消息时使用的channel。
Golang 就是借用CSP模型的一些概念为之实现并发进行理论支持,其实从实际上出发,go语言并没有,完全实现了CSP模型的所有理论,仅仅是借用了 process和channel这两个概念。process是在go语言上的表现就是 goroutine 是实际并发执行的实体,每个实体之间是通过channel通讯来实现数据共享。
Go语言的CSP模型是由协程Goroutine与通道Channel实现:
[*]Go协程goroutine: 是一种轻量线程,它不是操作系统的线程,而是将一个操作系统线程分段使用,通过调度器实现协作式调度。是一种绿色线程,微线程,它与Coroutine协程也有区别,能够在发现堵塞后启动新的微线程。
[*]通道channel: 类似Unix的Pipe,用于协程之间通讯和同步。协程之间虽然解耦,但是它们和Channel有着耦合。
channel
make (chan元素类型,「缓冲模型」)
[*]有缓冲通道 make(chan int)
[*]无缓冲通道 make(chan int,2)
https://cdn.nlark.com/yuque/0/2023/png/35902537/1685501452374-d28de138-6303-475d-a8e3-3efb6e4ac49b.png#averageHue=%23f6f6f6&clientId=ue0ed4711-e7f1-4&from=paste&height=298&id=u6db4cfc6&originHeight=744&originWidth=1270&originalType=binary&ratio=2.5&rotation=0&showTitle=false&size=99713&status=done&style=none&taskId=uca70c053-c1c5-4166-b104-f3083bc3fd3&title=&width=508
goroutine 和 channel 是 Go 语言并发编程的 两大基石。Goroutine 用于执行并发任务,channel 用于 goroutine 之间的同步、通信。
Channel 在 gouroutine 间架起了一条管道,在管道里传输数据,实现 gouroutine 间的通信;由于它是线程安全的,所以用起来非常方便;channel 还提供 “先进先出” 的特性;它还能影响 goroutine 的阻塞和唤醒。
不要通过共享内存来通信,而要通过通信来实现内存共享。
这就是 Go 的并发哲学,它依赖 CSP 模型,基于 channel 实现。
因为 channel 是一个引用类型,所以在它被初始化之前,它的值是 nil,channel 使用 make 函数进行初始化。可以向它传递一个 int 值,代表 channel 缓冲区的大小(容量),构造出来的是一个缓冲型的 channel;不传或传 0 的,构造的就是一个非缓冲型的 channel。
两者有一些差别:非缓冲型 channel 无法缓冲元素,对它的操作一定顺序是 “发送 -> 接收 -> 发送 -> 接收 -> ……”,如果想连续向一个非缓冲 chan 发送 2 个元素,并且没有接收的话,第一次一定会被阻塞;对于缓冲型 channel 的操作,则要 “宽松” 一些,毕竟是带了 “缓冲” 光环。
对 chan 的发送和接收操作都会在编译期间转换成为底层的发送接收函数。
Channel 分为两种:带缓冲、不带缓冲。对不带缓冲的 channel 进行的操作实际上可以看作 “同步模式”,带缓冲的则称为 “异步模式”。
同步模式下,发送方和接收方要同步就绪,只有在两者都 ready 的情况下,数据才能在两者间传输(后面会看到,实际上就是内存拷贝)。否则,任意一方先行进行发送或接收操作,都会被挂起,等待另一方的出现才能被唤醒。
异步模式下,在缓冲槽可用的情况下(有剩余容量),发送和接收操作都可以顺利进行。否则,操作的一方(如写入)同样会被挂起,直到出现相反操作(如接收)才会被唤醒。
package mainfunc CalSquare() { src := make(chan int) dest := make(chan int, 3) go func() { defer close(src) //Go语言的 defer 语句会将其后面跟随的语句进行延迟处理, //在 defer 归属的函数即将返回时,将延迟处理的语句按 defer //的逆序进行执行,也就是说,先被 defer 的语句最后被执行, //最后被 defer 的语句,最先被执行。 for i := 0; i < 10; i++ { src
页:
[1]