go多线程

go多线程

go语言中不是通过共享内存通信,而是通过通信共享内存

协程goroutine

goroutine,即轻量级线程,创建Goroutine的成本很小,它就是一段代码,一个函数入口。以及在堆上为其分配的一个堆栈(初始大小为4K,会随着程序的执行自动增长删除)。因此它非常廉价,Go应用程序可以并发运行数千个Goroutines。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"net/http"
"time"
)

func main() {
// 通过协程同时执行多个方法
start := time.Now()

apis := []string{
"https://management.azure.com",
"https://dev.azure.com",
"https://api.github.com",
"https://outlook.office.com/",
"https://api.somewhereintheinternet.com/",
"https://graph.microsoft.com",
}
for _, api := range apis {
go checkAPI(api) // 每次请求都创建一个协程
}
time.Sleep(3 * time.Second)

elapsed := time.Since(start)
fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}

func checkAPI(api string) {
_, err := http.Get(api)
if err != nil {
fmt.Printf("ERROR: %s is down!\n", api)
return
}
fmt.Printf("SUCCESS: %s is up and running!\n", api)
}

协程通信channel

使用管道

1
2
3
4
5
6
7
8
9
10
11
12
// 创建了可以传递int类型数据的管道
ch := make(chan int)

ch <- x // 发送数据到管道
x = <-ch // 从管道接收数据
<-ch // 读取但不接收数据

// 关闭管道
close(ch)

func send(ch chan<- string){} // 只写管道
func read(ch <-chan string){} // 只读管道

案例1:无缓冲channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"fmt"
"net/http"
"time"
)

func main() {
start := time.Now()

apis := []string{
"https://management.azure.com",
"https://dev.azure.com",
"https://api.github.com",
"https://outlook.office.com/",
"https://api.somewhereintheinternet.com/",
"https://graph.microsoft.com",
}

ch := make(chan string) // 创建管道
for _, api := range apis {
go checkAPI(api, ch) // 每次请求都创建一个协程
}
for i := 0; i < len(apis); i++ {
fmt.Print(<-ch) // 会阻塞等待
}

elapsed := time.Since(start)
fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}

func checkAPI(api string, ch chan string) {
_, err := http.Get(api)
if err != nil {
ch <- fmt.Sprintf("ERROR: %s is down!\n", api)
return
}
ch <- fmt.Sprintf("SUCCESS: %s is up and running!\n", api)
}

案例2:有缓冲channel

1
2
3
// 可以存10条数据的管道
// 往满了的管道里发送数据会阻塞
ch := make(chan string, 10)

区别

无缓冲的channel在发送数据时,发送者会被阻塞,直到有接收者接收数据。

有缓冲的channel则当缓冲区未满的时候会继续往下执行

多路复用select

语法和switch类似,作用是当case后面的对管道的操作执行成功后,会执行对应的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"fmt"
"time"
)

func process(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "Done processing!"
}

func replicate(ch chan string) {
time.Sleep(1 * time.Second)
ch <- "Done replicating!"
}

func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go process(ch1)
go replicate(ch2)

for i := 0; i < 2; i++ {
select {
case process := <-ch1:
fmt.Println(process)
case replicate := <-ch2:
fmt.Println(replicate)
}
}
}

go多线程
http://xwww12.github.io/2023/09/24/go/go多线程/
作者
xw
发布于
2023年9月24日
许可协议