for _, r := range -\|/
{
fmt.Printf(“\r%c”, r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
从执行结果来看,成功计算出了斐波那契数列的值,说明程序在 spinner
处并没有阻塞,而且 spinner
函数还一直在屏幕上打印提示字符,说明程序正在执行。
当计算完斐波那契数列的值,main
函数打印结果并退出,spinner
也跟着退出。
再来看一个例子,循环执行 10 次,打印两个数的和:
package main
import “fmt”
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
for i := 0; i < 10; i++ {
go Add(i, i)
}
}
有问题了,屏幕上什么都没有,为什么呢?
这就要看 Go 程序的执行机制了。当一个程序启动时,只有一个 goroutine 来调用 main
函数,称为主 goroutine。新的 goroutine 通过 go
关键词创建,然后并发执行。当 main
函数返回时,不会等待其他 goroutine 执行完,而是直接暴力结束所有 goroutine。
那有没有办法解决呢?当然是有的,请往下看。
channel
一般写多进程程序时,都会遇到一个问题:进程间通信。常见的通信方式有信号,共享内存等。goroutine 之间的通信机制是通道 channel。
使用 make
创建通道:
ch := make(chan int) // ch 的类型是 chan int
通道支持三个主要操作:send
,receive
和 close
。
ch <- x // 发送
x = <-ch // 接收
<-ch // 接收,丢弃结果
close(ch) // 关闭
无缓冲 channel
make
函数接受两个参数,第二个参数是可选参数,表示通道容量。不传或者传 0 表示创建了一个无缓冲通道。
无缓冲通道上的发送操作将会阻塞,直到另一个 goroutine 在对应的通道上执行接收操作。相反,如果接收先执行,那么接收 goroutine 将会阻塞,直到另一个 goroutine 在对应通道上执行发送。
所以,无缓冲通道是一种同步通道。
下面我们使用无缓冲通道把上面例子中出现的问题解决一下。
package main
import “fmt”
func Add(x, y int, ch chan int) {
z := x + y
ch <- z
}
func main() {
ch := make(chan int)
for i := 0; i < 10; i++ {
go Add(i, i, ch)
}
for i := 0; i < 10; i++ {
fmt.Println(<-ch)
}
}
可以正常输出结果。
主 goroutine 会阻塞,直到读取到通道中的值,程序继续执行,最后退出。
缓冲 channel
创建一个容量是 5 的缓冲通道:
ch := make(chan int, 5)
缓冲通道的发送操作在通道尾部插入一个元素,接收操作从通道的头部移除一个元素。如果通道满了,发送会阻塞,直到另一个 goroutine 执行接收。相反,如果通道是空的,接收会阻塞,直到另一个 goroutine 执行发送。
有没有感觉,其实缓冲通道和队列一样,把操作都解耦了。
单向 channel
类型 chan<- int
是一个只能发送的通道,类型 <-chan int
是一个只能接收的通道。
任何双向通道都可以用作单向通道,但反过来不行。
还有一点需要注意,close
只能用在发送通道上,如果用在接收通道会报错。
看一个单向通道的例子:
package main
import “fmt”
func counter(out chan<- int) {
for x := 0; x < 10; x++ {
out <- x
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for v := range in {
out <- v * v
}
close(out)
}
func printer(in <-chan int) {
for v := range in {
fmt.Println(v)
}
}
func main() {
n := make(chan int)
s := make(chan int)
go counter(n)
go squarer(s, n)
printer(s)
}
sync
sync 包提供了两种锁类型:sync.Mutex
和 sync.RWMutex
,前者是互斥锁,后者是读写锁。
当一个 goroutine 获取了 Mutex
后,其他 goroutine 不管读写,只能等待,直到锁被释放。
package main
import (
“fmt”
“sync”
“time”
)
func main() {
var mutex sync.Mutex
wg := sync.WaitGroup{}
// 主 goroutine 先获取锁
fmt.Println(“Locking (G0)”)
mutex.Lock()
fmt.Println(“locked (G0)”)
wg.Add(3)
for i := 1; i < 4; i++ {
go func(i int) {
// 由于主 goroutine 先获取锁,程序开始 5 秒会阻塞在这里
fmt.Printf(“Locking (G%d)\n”, i)
mutex.Lock()
fmt.Printf(“locked (G%d)\n”, i)
time.Sleep(time.Second * 2)
mutex.Unlock()
fmt.Printf(“unlocked (G%d)\n”, i)
wg.Done()
}(i)
}
// 主 goroutine 5 秒后释放锁
time.Sleep(time.Second * 5)
fmt.Println(“ready unlock (G0)”)
mutex.Unlock()
fmt.Println(“unlocked (G0)”)
wg.Wait()
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)
片转存中…(img-rZRlmcCO-1713580859894)]
[外链图片转存中…(img-QOeN3T1x-1713580859895)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)