day8 golang-chan-协程-定时器-锁-等待组
2023-04-14 20:05 dribs 阅读(28) 评论(0) 编辑 收藏 举报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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | package main import ( "fmt" "math/rand" "sync" "sync/atomic" "time" ) func example1() { //不要这样写,阻塞就死无法解除,零值nil var c1 chan int fmt.Printf( "%d,%d,%v" , len(c1), cap(c1), c1) //c1 <- 1 //阻塞不报错,由于没有初始化容器,1塞不进去,死锁。 <-c1 //也阻塞,什么都拿不出来,死锁 fmt.Println( "111111" ) //上面阻塞了死锁了 这个是不能打印出来的 } func example2() { //通过make创建 //非缓冲通道,容量为0,也叫同步通道。发送第一个元素时,如果没有接收操作就立即阻塞,知道接收,同样接收时,如果没有数据发送就立即阻塞,知道数据发送 c2 := make(chan int , 0) c3 := make(chan int ) fmt.Printf( "c2: %d,%d,%v\n" , len(c2), cap(c2), c2) //c2: 0,0,0xc0000103c0 fmt.Printf( "c3: %d,%d,%v\n" , len(c3), cap(c3), c3) //c3: 0,0,0xc000010420 //缓冲通道 c4 := make(chan int , 8) fmt.Printf( "c4: %d,%d,%v\n" , len(c4), cap(c4), c4) //c4: 0,8,0xc0000240a0 //往通道发送数据 c4 <- 1 c4 <- 2 fmt.Printf( "c4: %d,%d,%v\n" , len(c4), cap(c4), c4) //c4: 2,8,0xc0000240a0 //从通道拿数据接收数据 <-c4 //拿走,扔了 t := <-c4 //拿出来赋值给t fmt.Printf( "%T,%[1]v" , t) //int,2 } //单项通道 chan<- type 只往一个chan里面写,<-chan type 只从chan里面拿 func produce(ch chan<- int ) { for { ch <- rand.Intn(10) time.Sleep(time.Second) //关闭通道,只有发送方才能关闭,一旦关闭,在发送数据就panic, //如果去掉for,通道只有一个数据,关闭通道,接收者依然可以访问关闭的通道而不阻塞, //t,ok :=<-ch获取数据失败,ok为false,返回零值 //close(ch) //如果再次关闭直接panic } } func consume(ch <-chan int ) { for { t, ok := <-ch fmt.Println( "包子被吃了,id:" , t, ok) } } func example3() { //等待组 var wg sync.WaitGroup wg.Add(1) fmt.Println( "准备吃包子了" ) c := make(chan int ) go produce(c) go consume(c) wg.Wait() } func example4() { //遍历通道 //缓冲的关闭的通道 c1 := make(chan int , 5) c1 <- 1 c1 <- 2 c1 <- 3 close(c1) fmt.Println(<-c1, "故意从通道放走一个" ) for v := range c1 { fmt.Println(v) } fmt.Println( "end,不关闭通道,你看不见我" ) } func example5() { //1、非缓冲的未关闭的通道 //相当于一个无限元素的通道,迭代不完,阻塞在等下一个元素到达 //2、非缓冲关闭的通道 //关闭后,通道不能在进入新的元素,那么相当于遍历有限个元素容器,遍历完就结束了 c1 := make(chan int ) //非缓冲通道 go func() { defer close(c1) count := 1 for i := 0; i < 5; i++ { time.Sleep(3 * time.Second) c1 <- count count++ } }() for v := range c1 { fmt.Println( "v print:" , v) } fmt.Println( "不关闭通道,我就死锁,你就看不到我" ) } func example6() { //定时器 go func() { t := time.NewTicker(2 * time.Second) //定义2秒的定时器 for { fmt.Println( "我是Ticker定时器阻塞2s的:" , <-t.C) //通道阻塞住每隔2秒就接收一次 } }() go func() { t := time.NewTimer(5 * time.Second) for { fmt.Println( "Timer我开始了" ) fmt.Println( "我是Timer定时器阻塞5s的:" , <-t.C) //通道阻塞5s后只接收一次,再来就阻塞住 fmt.Println( "Timer我接收完了" ) } fmt.Println( "Timer我接收完了,但我不会打印出来" ) }() time.Sleep(100 * time.Second) } func example7() { //通道死锁 c1 := make(chan int ) c1 <- 1 //当前协程阻塞。无人能解。死锁 } func example8() { //struct{}型通道 //如果一个结构体类型就是struct{},说明该结构体的实例没有数据成员,也就是实例内存占用为0,节约内存,仅仅是为了传递一个信号标志 flag := make(chan struct {}) //比chan bool生内存 go func() { time.Sleep(3 * time.Second) flag <- struct {}{} //无数据成员的结构体实例 }() fmt.Printf( "等到了信号,%T,%[1]v\n" , flag) } func example9() { //通道多路复用 count := make(chan int , 4) fin := make(chan struct {}) go func() { defer func() { fin <- struct {}{} }() for i := 0; i < 10; i++ { count <- i time.Sleep(time.Second) } }() for { select { case n := <-count: fmt.Println( "count:" , n) case <-fin: fmt.Println( "收到退出信号,跳出循环" ) goto END } } END: fmt.Println( "我跳出来了" ) } func inc(count *int64) { for i := 0; i < 100000; i++ { *count += 1 } } func inc2(count *int64, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 100000; i++ { *count += 1 } } func inc3(count *int64, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 100000; i++ { atomic.AddInt64(count, 1) } } func inc4(count *int64, mx *sync.Mutex, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 100000; i++ { mx.Lock() *count++ mx.Unlock() } } func inc5(wg *sync.WaitGroup, ch chan int64) { defer wg.Done() for i := 0; i < 100000; i++ { t := <-ch t++ ch <- t } } func example10() { //通道并发 锁,加了锁会影响并行效率保证了逻辑正确 var count int64 = 0 start := time.Now() //串行没有并发的时候 //inc(&count) //inc(&count) //inc(&count) //inc(&count) //inc(&count) // //fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine()) //fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了1590ms count正确 //for i := 0; i < 5; i++ { // go inc(&count) // // //fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine()) //} //fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了0-541ms count不对 //var wg sync.WaitGroup //wg.Add(5) //for i := 0; i < 5; i++ { // go inc2(&count, &wg) //} //// fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine()) //wg.Wait() //fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了512ms,count is:14466 ////上面两个例子加了go协程后 最终得到结果完全不对,原因是count++不是原子操作,会被打断。1、原子操作 2、加锁保证结果正确 //==================原子操作 //var wg sync.WaitGroup //wg.Add(5) //for i := 0; i < 5; i++ { // go func() { // defer wg.Done() // for i := 0; i < 100000; i++ { // atomic.AddInt64(&count, 1) // } // }() // //或者把上面的匿名函数扔出去,把count和wg传进去 // //go inc3(&count, &wg) //} //wg.Wait() //fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //12705ms count:500000 //=======互斥锁 //var wg sync.WaitGroup //var mx sync.Mutex //wg.Add(5) //for i := 0; i < 5; i++ { // go inc4(&count, &mx, &wg) //} ////fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine()) //wg.Wait() //fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了44866ms,count is:500000 //======使用管道 var wg sync.WaitGroup ch := make(chan int64, 1) ch <- 0 wg.Add(5) for i := 0; i < 5; i++ { go inc5(&wg, ch) } wg.Wait() fmt.Println(count) fmt.Printf( "执行了%dms,count is:%d\n" , time.Since(start).Microseconds(), <-ch) //执行了297097ms,count is:500000 } func main() { //example1() //example2() //example3() //time.Sleep(100 * time.Second) //如果开了协程,主协程运行完程序就结束了,笨方法是sleep一会;第二个方法加等待组 //example4() //example5() //example6() //example7() //example8() //example9() example10() } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架