并发
并发性意味着应用程序同时(并发地)处理多个任务。
如果计算机只有一个CPU,应用程序可能不会在同一时间处理多个任务,但是在应用程序内一次处理多个任务。
在开始下一个任务之前,它不会完全完成一个任务。相反,CPU在不同的任务之间切换,直到任务完成。
即使只有一个线程在其中运行,也可以有一个并发应用程序。
并行
并行性意味着应用程序将其任务分割成更小的子任务,这些子任务可以并行处理,例如在多个cpu上同时处理。
为了实现真正的并行,您的应用程序必须有多个线程在运行,或者至少能够调度任务在其他线程、进程、cpu、显卡等中执行。
栗子:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发 | 并行 |
代码模拟使用咖啡机
Erlang 之父 Joe Armstrong 用一张5岁小孩都能看懂的图解释了并发与并行的区别
- 并发 是两个队列 交替 使用 一台 咖啡机
- 并行 是两个队列 同时 使用 两台 咖啡机
如果串行,一个队列使用一台咖啡机,那么哪怕前面那个人便秘了去厕所呆半天,后面的人也只能死等着他回来才能去接咖啡,这效率无疑是最低的
串行使用咖啡机
package main
import (
"fmt"
"time"
)
var (
ListA = []string{"小明", "小红", "小王", "宝宝", "娜娜", "拉拉", "琪琪"}
ListB = []string{"威廉", "巴克", "查理", "哈利", "哈瑞", "米菲", "珍妮", "米奇"}
)
func task(list []string, from string) {
i := 0
for {
if (i >= len(list)) {
if (from == "A") {
fmt.Println("A队人员已排队完~~~")
} else if (from == "B") {
fmt.Println( "B队人员已排队完~~~")
}
break;
}
if (from == "A") {
fmt.Printf("A队[%d]: %s 使用咖啡机 \n", i+1, list[i])
} else if (from == "B") {
fmt.Printf("B队[%d]: %s 使用咖啡机 \n", i+1, list[i])
}
i++;
time.Sleep(time.Second)
}
}
func main() {
task(ListA, "A")
task(ListB, "B")
// 主协程
for {
;
}
/**
A队[1]: 小明 使用咖啡机
A队[2]: 小红 使用咖啡机
A队[3]: 小王 使用咖啡机
A队[4]: 宝宝 使用咖啡机
A队[5]: 娜娜 使用咖啡机
A队[6]: 拉拉 使用咖啡机
A队[7]: 琪琪 使用咖啡机
A队人员已排队完~~~
B队[1]: 威廉 使用咖啡机
B队[2]: 巴克 使用咖啡机
B队[3]: 查理 使用咖啡机
B队[4]: 哈利 使用咖啡机
B队[5]: 哈瑞 使用咖啡机
B队[6]: 米菲 使用咖啡机
B队[7]: 珍妮 使用咖啡机
B队[8]: 米奇 使用咖啡机
B队人员已排队完~~~
*/
}
并发使用一台咖啡机
可能会遇见如下情况:
A 队列被 B队列小伙伴插队了,这就是条件竞争
A 队列有一位英俊小伙和 B 队列一位女神并排,轮到他们俩的时候两人互看对方一眼,互让一步同时彬彬有礼的说了一句:你先。这个时候活锁出现了。
B 队列一位小伙伴,是代替其他朋友来打咖啡,他带了一箱子杯子,终于轮到他了。这时饥饿就出现了。
A 队列一位小伙伴喜欢摩卡,卡布奇诺混合咖啡,B 队列一位小伙伴喜欢拿铁,摩卡混合咖啡。当他们互相等待对方摩卡出杯的时候后,死锁来了。 ```go package main
import ( “fmt” “time” )
var ( ListA = []string{“小明”, “小红”, “小王”, “宝宝”, “娜娜”, “拉拉”, “琪琪”} ListB = []string{“威廉”, “巴克”, “查理”, “哈利”, “哈瑞”, “米菲”, “珍妮”, “米奇”} )
func task(list []string, from string) {
i := 0
for {
if (i >= len(list)) {
if (from == “A”) {
fmt.Println(“A队人员已排队完~”)
} else if (from == “B”) {
fmt.Println( “B队人员已排队完~”)
}
break;
}
if (from == “A”) {
fmt.Printf(“A队[%d]: %s 使用咖啡机 \n”, i+1, list[i])
} else if (from == “B”) {
fmt.Printf(“B队[%d]: %s 使用咖啡机 \n”, i+1, list[i])
}
i++;
time.Sleep(time.Second)
}
}
func main() { // 子协程 go task(ListA, “A”) go task(ListB, “B”)
// 主协程
for {
;
}
/**
A队[1]: 小明 使用咖啡机
B队[1]: 威廉 使用咖啡机
B队[2]: 巴克 使用咖啡机
A队[2]: 小红 使用咖啡机
A队[3]: 小王 使用咖啡机
B队[3]: 查理 使用咖啡机
B队[4]: 哈利 使用咖啡机
A队[4]: 宝宝 使用咖啡机
A队[5]: 娜娜 使用咖啡机
B队[5]: 哈瑞 使用咖啡机
B队[6]: 米菲 使用咖啡机
A队[6]: 拉拉 使用咖啡机
A队[7]: 琪琪 使用咖啡机
B队[7]: 珍妮 使用咖啡机
B队[8]: 米奇 使用咖啡机
A队人员已排队完~~~
B队人员已排队完~~~
*/
}
<a name="aT5Gc"></a>
## 协程的并发执行
同时启动 7 个子协程,由于它们是并发执行的,执行的先后顺序无法保证
```go
package main
import (
"fmt"
"time"
)
var (
ListA = []string{"小明", "小红", "小王", "宝宝", "娜娜", "拉拉", "琪琪"}
)
func task(i int) {
fmt.Printf("A队[%d]: %s 使用咖啡机 \n", i, ListA[i])
time.Sleep(time.Second)
}
func main() {
for i:=0; i<len(ListA); i++ {
go task(i)
}
for {
;
}
/*
A队[0]: 小明 使用咖啡机
A队[5]: 拉拉 使用咖啡机
A队[2]: 小王 使用咖啡机
A队[3]: 宝宝 使用咖啡机
A队[4]: 娜娜 使用咖啡机
A队[6]: 琪琪 使用咖啡机
A队[1]: 小红 使用咖啡机
*/
}