1.并发是什么?
并发是指<立即>处理多个任务的能力。
比如你的双手可以同时做两件事,比如吃饭这件事就是并发,吃饭这个过程中,可以同时吃几种菜,甚至喝汤,这个过程就是一个多任务并发的过程,但是并发在时间上是不能同时进行的
2.并发和并行的却别
并行是指<同时>处理多个任务。这听起来和并发差不多,但其实完全不同。
一边吃饭一边听音乐,听音乐和吃饭可以在时间上同时进行
3.并行不一定有并发效率高
并行不一定会加快运行速度,因为并行运行的组件之间可能需要相互通信。在我们浏览器的例子里,当文件下载完成后,应当对用户进行提醒,比如弹出一个窗口。于是,在负责下载的组件和负责渲染用户界面的组件之间,就产生了通信。在并发系统上,这种通信开销很小。但在多核的并行系统上,组件间的通信开销就很高了。所以,并行不一定会加快运行速度!
4.Go 协程是什么?
Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。
5.go协程 和 线程的优势对比
相比线程而言,Go 协程的成本极低。堆栈大小只有若干 kb,并且可以根据应用的需求进行增减。而线程必须指定堆栈的大小,其堆栈是固定不变的。 Go 协程会复用(Multiplex)数量更少的 OS 线程。即使程序有数以千计的 Go 协程,也可能只有一个线程。如果该线程中的某一 Go 协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个 OS 线程,并把其余 Go 协程都移动到这个新的 OS 线程。所有这一切都在运行时进行,作为程序员,我们没有直接面临这些复杂的细节,而是有一个简洁的 API 来处理并发。 Go 协程使用信道(Channel)来进行通信。信道用于防止多个协程访问共享内存时发生竞态条件(Race Condition)。信道可以看作是 Go 协程之间通信的管道。我们会在下一教程详细讨论信道。
6.如何启动一个协程
调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
下面再看一个例子
package main
import "fmt"
func print(i int){
fmt.Println(i)
}
func main() {
for i := 1 ;i < 40 ;i++{
go print(i)
}
}
image.png
我们研究一下这个现象
import "fmt"
func print(i int){
fmt.Println(i)
}
func main() {
for i := 1 ;i < 40 ;i++{
go print(i)
fmt.Printf("第 %d 循环",i)
}
}
image.png
我们根据这个结果能得出三个结论?
go 协程的理论如下
注意 main 函数其实调用也是一个协程,它被称为 主协程
package main
import "fmt"
func print(i int){
fmt.Println(i)
}
func main() {
go print(100)
}
猜猜会输出什么?
image.png
什么都不会输出,因为 print(100)还没执行完毕,main函数已经结束了,所以它的子协程也不会执行了
那怎么解决这个问题呢?
信道可用于在其他协程结束执行之前,阻塞 Go 主协程。请查看信道章节
package main
import (
"fmt"
"time"
)
func print(num int){
for i := 1 ;i < 10 ;i++ {
fmt.Printf("%d - 第 %d 循环 \n",num, i)
}
}
func main() {
go print(1)
go print(2)
time.Sleep(1000 * time.Millisecond)
}
image.png
由于main 函数 会很快执行完毕 导致子协程 停止工作,所以我们加一个3秒的延时 让main 函数休眠,从而让它的两个个子协程能够执行执行完毕