day8 golang-chan-协程-定时器-锁-等待组
2023-04-14 20:05 dribs 阅读(27) 评论(0) 编辑 收藏 举报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() }