在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
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 }
|
请发表评论