1 package main
2
3 import (
4 "fmt"
5 "runtime"
6 "strconv"
7 "sync"
8 "time"
9 )
10
11 // sync包中的WaitGroup实现了一个类似任务队列的结构,你可以向队列中加入任务,
12 // 任务完成后就把任务从队列中移除,如果队列中的任务没有全部完成,队列就会触发阻塞以阻止程序继续运行
13 var wg sync.WaitGroup // 实现goroutine的同步
14 var lock sync.Mutex // 互斥锁
15 var rwlock sync.RWMutex // 读写互斥锁
16
17 // 1. 启动单个 goroutine
18 func hello() {
19 fmt.Println("Hello!")
20 }
21
22 func f1() {
23 go hello()
24 fmt.Println("Hello Goroutine Down!")
25 time.Sleep(time.Second)
26 /*
27 // 注意顺序
28 Hello Goroutine Down!
29 Hello!
30 */
31 }
32
33 // 2. 启动多个 goroutine
34 func hello1(i int) {
35 defer wg.Done() // goroutine结束就登记-1
36 fmt.Println("Hello1 Goroutine: ", i)
37 }
38
39 func f2() {
40 for i := 0; i < 10; i++ {
41 wg.Add(1) // 启动一个goroutine就登记+1
42 go hello1(i)
43 /*
44 Hello1 Goroutine: 0
45 Hello1 Goroutine: 9
46 Hello1 Goroutine: 4
47 Hello1 Goroutine: 2
48 Hello1 Goroutine: 5
49 Hello1 Goroutine: 6
50 Hello1 Goroutine: 1
51 Hello1 Goroutine: 8
52 Hello1 Goroutine: 7
53 Hello1 Goroutine: 3
54 */
55 }
56 wg.Wait() // 等待所有登记的goroutine都结束
57 }
58
59 // 3. GOMAXPROCS
60 /*
61 Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。
62 一个操作系统线程对应用户态多个goroutine。
63 go程序可以同时使用多个操作系统线程。
64 goroutine和OS线程是多对多的关系,即m:n。
65 */
66
67 func a() {
68 for i := 1; i < 10; i++ {
69 fmt.Println("A:", i)
70 }
71 }
72
73 func b() {
74 for i := 1; i < 10; i++ {
75 fmt.Println("B:", i)
76 }
77 }
78
79 func f3() {
80 v := runtime.GOMAXPROCS(2) // 设置 1 顺序输出; 2 随机交替输出
81 fmt.Println("默认机器核心数是:", v)
82 go a()
83 go b()
84 time.Sleep(time.Second)
85 }
86
87 // 4. 通道 Go 提倡通过通信共享内存而不是通过共享内存而实现通信
88 // var ch chan int
89 // make(chan 元素类型, [缓冲大小])
90
91 // 无缓冲区通道,同步通道
92 func syncChannel(c chan int) {
93 ret := <-c
94 fmt.Println("通道取出值:", ret)
95 }
96
97 func f4() {
98 ch := make(chan int)
99 go syncChannel(ch) // 启用goroutine从通道接收值
100 ch <- 10
101 fmt.Println("通道发送值成功!")
102 }
103
104 // 5 有缓冲区的通道
105 func f5() {
106 ch := make(chan int, 2) // 创建一个容量位2的 有缓冲区通道
107 ch <- 10
108 fmt.Printf("ch 的长度:%d,ch 的容量:%d\n", len(ch), cap(ch)) // ch 的长度:1,ch 的容量:2
109 }
110
111 // 6. 从通道循环取值
112 func f6() {
113 ch1 := make(chan int)
114 ch2 := make(chan int)
115
116 // 给 ch2 赋值
117 go func() {
118 for i := 0; i < 3; i++ {
119 ch1 <- i
120 }
121 close(ch1)
122 }()
123
124 go func() {
125 // 从通道取值的第一种方法
126 for {
127 i, ok := <-ch1
128 if !ok { // 判断通道是否为空
129 break
130 }
131 ch2 <- i * i
132 }
133 close(ch2)
134 }()
135
136 // 通道 ch3 关闭会退出 for range 循环
137 for i := range ch2 {
138 // 从通道取值的第二种方法
139 fmt.Println("从ch2中取出值:", i)
140 // 从ch2中取出值: 0
141 // 从ch2中取出值: 1
142 // 从ch2中取出值: 4
143 }
144 }
145
146 // 7. 单向通道
147 func outCh(out <-chan int) {
148 for i := range out {
149 println("输出通道, 值:", i)
150 }
151 }
152
153 func inCh(in chan<- int) {
154 for i := 0; i < 3; i++ {
155 in <- i
156 }
157 close(in)
158 }
159
160 func square(in chan<- int, out <-chan int) {
161 for i := range out {
162 in <- i * i
163 }
164 close(in)
165 }
166
167 func f7() {
168 ch1 := make(chan int)
169 ch2 := make(chan int)
170
171 go inCh(ch1) // 限制通道在函数中只能接收
172 go square(ch2, ch1) // 限制一个可发送、一个可接收的通道
173 outCh(ch2) // 限制通道在函数中只能发送
174 }
175
176 // 8. 线程池 worker pool
177 func worker(id int, jobs <-chan int, results chan<- int) {
178 for j := range jobs {
179 fmt.Printf("Worder %d start job %d\n", id, j)
180 time.Sleep(time.Second)
181 fmt.Printf("Worder %d end job %d\n", id, j)
182 results <- 2 * j
183 }
184 }
185
186 func f8() {
187 jobs := make(chan int, 100)
188 results := make(chan int, 100)
189 // 开启三个 goroutine
190 for w := 1; w < 4; w++ {
191 go worker(w, jobs, results)
192 }
193 // 5个任务
194 for j := 1; j < 6; j++ {
195 jobs <- j
196 }
197 close(jobs)
198 // 打印结果
199 for a := 1; a < 6; a++ {
200 <-results
201 }
202 // worker:3 start job:1
203 // worker:1 start job:2
204 // worker:2 start job:3
205 // worker:1 end job:2
206 // worker:1 start job:4
207 // worker:3 end job:1
208 // worker:3 start job:5
209 // worker:2 end job:3
210 // worker:1 end job:4
211 // worker:3 end job:5
212 }
213
214 // 9. select 多路复用
215 // 可处理一个或多个channel的发送/接收操作。
216 // 如果多个case同时满足,select会随机选择一个。
217 // 对于没有case的select{}会一直等待,可用于阻塞main函数。
218 func f9() {
219 ch := make(chan int, 1)
220 for i := 0; i < 10; i++ {
221 select {
222 // ch 能取出值
223 case x := <-ch:
224 fmt.Printf("%d\t", x) // 0 2 4 6 8
225 // 值能放进 ch
226 case ch <- i:
227 }
228 }
229 }
230
231 // 10. 互斥锁
232 // 不加锁的情况
233 func add(x *int64) {
234 for i := 0; i < 5000; i++ {
235 *x += 1
236 }
237 wg.Done()
238 }
239
240 // 加锁的情况
241 func add1(x *int64) {
242 for i := 0; i < 5000; i++ {
243 lock.Lock()
244 *x += 1
245 lock.Unlock()
246 }
247 wg.Done()
248 }
249
250 func f10() {
251 var x int64
252 wg.Add(2)
253 // go add(&x) // 不加锁
254 // go add(&x) // 不加锁
255 go add1(&x)
256 go add1(&x)
257 wg.Wait()
258 fmt.Println(x)
259 }
260
261 // 11. 读写互斥锁
262 // 读写锁非常适合读多写少的场景,如果读和写的操作差别不大,读写锁的优势就发挥不出来
263
264 func write(x *int64) {
265 // lock.Lock()
266 rwlock.Lock() // 写锁
267 *x += 1
268 rwlock.Unlock()
269 // lock.Unlock()
270 wg.Done()
271 }
272
273 func read() {
274 // lock.Lock()
275 rwlock.RLock()
276 time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
277 rwlock.RUnlock()
278 // lock.Unlock()
279 wg.Done()
280 }
281
282 func f11() {
283 var x int64
284 start := time.Now()
285
286 for i := 0; i < 500; i++ {
287 wg.Add(1)
288 go write(&x)
289 }
290
291 for j := 0; j < 5000; j++ {
292 wg.Add(1)
293 go read()
294 }
295
296 wg.Wait()
297 fmt.Println(x, time.Since(start))
298 // lock: 500 6.0364704s
299 // rwlock: 500 10.9918ms
300 }
301
302 // 12. sync.map
303 func f12() {
304 var m = sync.Map{}
305 for i := 0; i < 100; i++ {
306 wg.Add(1)
307 go func(n int) {
308 key := strconv.Itoa(n)
309 m.Store(key, n) // map 存
310 value, _ := m.Load(key) // map 取
311 fmt.Printf("n=:%d,k=:%v,v:=%v\n", n, key, value)
312 wg.Done()
313 }(i)
314 }
315 wg.Wait()
316 }
317
318 func main() {
319 f12()
320 }