代码改变世界

day8 golang-chan-协程-定时器-锁-等待组

  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()
}

  

相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示