31_Go基础,并发

  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 }