代码改变世界

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