1.先要明白什么是并行,什么是并发
并发是因为咖啡机的有限,导致不得不使用频繁切换的方式,来服务不同队列的顾客
并行是因为咖啡机足够,所以可以一台咖啡机服务一个队列的顾客
2.golang语言高并发特性的来源
php,java,python等是通过多进程,多线程来实现并发的
但由于进程,线程是操作系统调度层面的,所以缺点就很明显
1.开辟需要的内存耗费大
2.切换进线程需要的内存耗费大
随着 web2.0 的出现高并发开始成为刚需
各个语言就开始出现用户级线程,轻量级线程,协程等概念,就是为了解决高并发
go设计出了内存占用仅 2k 的 gorotine【go的协程】,有开辟空间小,切换快的特点
3.使用 go 关键字启动协程
同步:main协程等待go协程运行完毕才会退出
异步:main协程不等待go协程运行完毕才会退出
由于main协程 与 go协程是异步运行的关系,所以下面的代码没机会打印出:xhz
注意:main协程 与 go协程是【主死从就死】的关系
package main
import "fmt"
// asynchronous异步的
func asyncPrint() {
fmt.Println("xhz")
}
func main() {
go asyncPrint()//go关键字即可启动一个go协程
}
3.1进阶---让main协程等待go协程
package main
import (
"fmt"
"time"
)
// asynchronous异步的
func asyncPrint() {
fmt.Println("xhz")
}
func main() {
go asyncPrint()
time.Sleep(1 * time.Second)
}
//xhz
只执行一次的函数建议写成匿名函数调用更好
func main() {
go func() {
fmt.Println("xhz")
}()
time.Sleep(1 * time.Second)
}
//xhz
3.2进阶---体会随意启动多个go协程
简单来说我们只需要在不同的协程中打印不同数字结论体会到启动了不同的协程
为什么打印不是0--99 ???
1.for 循环是main协程,打印是各个go协程,他们是异步的关系
2.比如 i=0 的循环里,由于是异步的,所以协程不一定是马上就执行内容的
3.就是说异步就意味着main协程管不了go协程的执行内容效率,人家可以快,可以慢
4.也许 i=30 的循环都开始了,那个 i=0 轮次里的打印才开始,可是这时 i 已经是30了
5.重复打印怎么回事:异步的协程管不了,那自然有可能出现协程一样快的情况了 , 并且此时一样快的 协程拿到的 i 是同一个轮次的数据
func main() {
for i := 0; i < 100; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(1 * time.Second)
}
//56
//3
//2
//34
//34
//35
//78
//100
//等等
3.3进阶---解决多个go协程打印不重复【闭包写法】
既然重复是因为打印的协程出现了一样快,并且拿的是同一个轮次的数据导致的
我们可以从这两个角度切入:
1.让每一个协程的执行速度不一样,更准确就是打印语句的时机不一样
2.让每一个协程的打印语句拿到的 i 是唯一的
1.目前想要做到精准控制协程的执行速度还没有语言【技术】做的到----
该方案不可取
----这里不考虑用sleep的这种强制性等待的方式来控制速度
2.给每一轮加一个临时变量temp存放当前轮次的 i ,然后打印就打印temp
func main() {
for i := 0; i < 100; i++ {
temp := i//i是协程的外部变量,所以可以构成【闭包】
go func() {
fmt.Println(temp)//闭包的特性致使打印语句会自行找外面的temp
}()
}
time.Sleep(1 * time.Second)
}
//3
//67
//34
//等等,解决了重复的问题
3.4进阶---解决多个go协程打印不重复【普通写法】
func main() {
for i := 0; i < 100; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
time.Sleep(1 * time.Second)
}
//4
//6
//99
//34
//等等,解决了重复的问题