zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", id, j)
    180         time.Sleep(time.Second)
    181         fmt.Printf("Worder %d end job %d
    ", 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	", 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, key, value)
    312             wg.Done()
    313         }(i)
    314     }
    315     wg.Wait()
    316 }
    317 
    318 func main() {
    319     f12()
    320 }
  • 相关阅读:
    豆瓣电台WP7客户端 MVVM重构记录之使用MVVM Light实现Event绑定
    使用DotNetOpenAuth来实现有道云笔记的授权
    豆瓣电台WP7客户端 MVVM重构记录之总结
    使用Npgsql连接Postgres
    WPF自定义一个MessageBox
    字符集其实很简单
    pytest封神之路第六步 断言技巧
    pytest封神之路第七步 用例查找原理
    一文搞懂Cookie,Session,Token,JWT
    典藏版Web功能测试用例库
  • 原文地址:https://www.cnblogs.com/luwei0915/p/15508430.html
Copyright © 2011-2022 走看看