package mainimport ( "fmt" "time")//go的协程和python的协程 - 网上有些人可能喜欢拿web框架来做性能对比//go的gin beego - flask/django + gevent性能对比 - 这个不科学的 uwsgi部署//flask/django 只是一个web框架 本身不提供 启动多线程 多进程来提高并发的能力 一般使用中间件//tornado sanic fastapi /asyncio 协程库 我们就不再使用python的多线程来对比了//只要大家懂了任何一门语言的协程 其他语言的协程都很好理解 GMP调度模型//1.大家都开启100w个协程 2.使用的简单性func p() { //fmt.Println("协程") for i := 1; i <= 100000; i++ { go func(n int) { fmt.Println(n) }(i) }}func main() { //闭包的特性 //主死从随 //go p() for i := 1; i <= 300000; i++ { go func(n int) { fmt.Println(n) time.Sleep(time.Second) }(i) } time.Sleep(time.Second*10)}//goroutine//python java c++ 多线程和多线程编程//go 语言诞生的比较晚 web 2.0 开发主键主流 高并发//多线程 - 每个线程占用的内存比较多 而且系统切换开销很大 上千绿程/轻量级线程 - 协程 - 用户态线程//go语言一开始的时候就没有让我们取实例化一个线程 - 协程 go 没有历史包袱//nodejs python3.6 那为什么go的协程为什么会这么火//python中有两种编程模式 1.多线程和多进程来进行并发编程 2. 使用协程进程并发编程 很多的库是基于多线程和多进程开发的//除非某一天所有的库 大部分的库都支持了协程
channel_test
package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroup//消费者消费func consumer(queue chan int) { defer wg.Done() //data := <-queue for { data,ok := <- queue if !ok{ break } fmt.Println(data) time.Sleep(time.Second) } //for data := range queue { // fmt.Println(data) // time.Sleep(time.Second) //} //fmt.Println(data)}func main() { /* channel 提供了一种通信机制 定向发消息 消息队列 python java */ //1. 定义一个channel var msg chan int //2. 初始化这个channel 两种方式 msg = make(chan int) //第一种初始化方式 无缓冲 //msg = make(chan int, 1) //第二种初始化方式 有缓冲空间 //3. 在go语言中 使用make初始化的有三种 slice map channel //go func(queue chan int) { // data := <-msg //将箭头右边的值放到左边 // fmt.Println(data) //}(msg) //msg <- 3 //fatal error: all goroutines are asleep - deadlock! wg.Add(1) go consumer(msg) msg <- 1 //你这个管道看起来好像就是一个有空间的数组 //go consumer(msg) msg <- 2 //fatal error: all goroutines are asleep - deadlock! //关闭channel 1.已经关闭的channel不能再发送数据了 2.已经关闭的channel 消费者能继续取数据吗? //2可以 直到数据取完为止 close(msg) //go consumer(msg) wg.Wait()}
channel_type
package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroupfunc consumer(queue <-chan int) { defer wg.Done() for { data, ok := <-queue if !ok { break } fmt.Println(data) time.Sleep(time.Second) }}func stock(queue chan <- int) { defer wg.Done() for { queue <- 1 time.Sleep(time.Second) }}func main() { //有没有缓冲 1.有缓冲 2.无缓冲 //双向的还是单向的 为了安全 还提供了单向channel var msg chan int //双向 可以把双向的channel赋值给单向的channel //var msg chan<- int //仅发送 //var msg <-chan int //仅接收 msg = make(chan int, 10) //data := <-msg //无效运算: <-msg (从仅发送类型 chan<- int 接收) //fmt.Println(data) wg.Add(1) go consumer(msg) //普通的channel 可以直接转换为单向的channel msg <- 1 close(msg) wg.Wait() /* 1 等待返回值 2 等待返回值 1 2 */}
context_test
package mainimport ( "context" "fmt" "sync" "time")var wg sync.WaitGroup//1.监控全局变量 来完成//2.通过channel//var stop bool//var stop chan bool = make(chan bool)//3.刚才的两种方式 这种方案并不统一 go1.7 context机制//父的context//父的context取消 那么这个父的context生成的context也会被取消func cpuInfo(ctx context.Context) { defer wg.Done() //ctx2, _ := context.WithCancel(ctx) context.WithDeadline(ctx, time.Now()) //a -> b -> c //context.WithTimeout() //context.WithValue() //go memoryInfo(ctx2) for { //if stop { // break //} //select { //case <-stop: // fmt.Println("退出CPU监控") // return //default: // time.Sleep(time.Second*2) // fmt.Println("CPU信息读取完成") //} select { case <-ctx.Done(): fmt.Println("监控退出") return default: time.Sleep(time.Second) fmt.Println("获取cpu信息成功") } }}func memoryInfo(ctx context.Context) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("监控内存退出") return default: time.Sleep(time.Second) fmt.Println("获取内存信息成功") } }}func main() { //现在启动一个goroutine去监控某台服务器的cpu使用情况 wg.Add(1) //ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) //有一种自动的机制在里面 //ctx, cancel := context.WithDeadline(context.Background(), time.Second*2) //有一种自动的机制在里面 go cpuInfo(ctx) time.Sleep(time.Second) cancel() //go memoryInfo(ctx) //现在希望可以中断CPU的信息读取 //time.Sleep(time.Second * 5) //cancel() //context在web开发中非常的常用 grpc 每个接口调用都会传递context gin的http接口也会有 //stop <- true //stop = true wg.Wait() fmt.Println("信息监控完成")}
deadlock_test
package mainimport ( "fmt" "sync")var wg sync.WaitGroupfunc consumer(msg chan int) { defer wg.Done() fmt.Println(<-msg)}func main() { var msg chan int msg = make(chan int) //当你进行 放数据到msg中的时候 这个时候会阻塞的 // 阻塞之前会获取一把锁 这把锁什么时候释放 肯定要等到数据被消费 wg.Add(1) go consumer(msg) msg <- 1 wg.Wait() //channel是多个goroutine之间线程安全 如何保证的呢? 使用锁 //如果你是没有缓冲的channel 在没有启动一个消费者之前 你放数据就会报错 //data := <-msg //fmt.Println(data) fmt.Println("hello world") fmt.Println()}
lock_test
package mainimport ( "fmt" "sync")/* 锁 - 资源竞争 1.按理说 最后的结果是0 2.实际的情况 1. 不是0 2.每次运行的结果不一样*/var total intvar wg sync.WaitGroup//互斥锁(同步问题) - 读写锁 同步数据 能不用锁就别用锁 - 性能下降//绝大多数的web系统来说 都是读多写少//有1w个人同时读数据库 A读的时候 B能读么? 为什么要加锁呢?100 - 1w - 100//立马我下单了 - 支付(会重新查一遍数据库 1w)//一定要加锁 写 和 读上面要加同一把锁 并发严重下降。//B读了一个数据,造成C读了数据产生影响吗? 一定是写和读之间造成的//如果这把锁之间可以做到 读之间不会产生影响 读写之间才会产生影响 那多好 读写锁var lock sync.Mutexfunc add() { defer wg.Done() for i := 0; i < 100000; i++ { //先把门锁上 lock.Lock() total += 1 //放开锁 lock.Unlock() //1.从total中取值 //2.将total+1 //3.将total+1的计算结果放入到total中 }}func sub(){ defer wg.Done() for i := 0; i < 100000; i++ { //先把门锁上 lock.Lock() total -= 1 lock.Unlock() //放开锁 }}func main() { wg.Add(2) go add() go sub() wg.Wait() fmt.Println(total)}
rwlock_test
package mainimport ( "fmt" "sync" "time")/* 锁 - 资源竞争 1.按理说 最后的结果是0 2.实际的情况 1. 不是0 2.每次运行的结果不一样*/var total intvar wg sync.WaitGroupvar lock sync.Mutex //互斥锁var rwLock sync.RWMutex //读写锁func read() { defer wg.Done() rwLock.RLock() fmt.Println("开始读取数据") time.Sleep(time.Second) fmt.Println("读取成功") rwLock.RUnlock()}func write(){ defer wg.Done() rwLock.Lock() fmt.Println("开始修改数据") time.Sleep(time.Second*2) fmt.Println("修改成功") rwLock.Unlock()}func main() { wg.Add(8) for i := 0; i < 5; i++ { go read() } for i := 0; i < 3; i++ { go write() } wg.Wait() fmt.Println(total)}
select_test
package mainimport ( "fmt" "time")func main() { /* go语言提供了select的功能,作用与channel之上的 多路复用 select 会随机公平的选择一个case语句执行 select的应用场景 1.timeout的超时机制 2.判断某个channel是否阻塞 */ //timeout := false timeout := make(chan bool, 2) go func() { //该goroutine如果执行时间超过了5s 那么就报告给主的goroutine //fmt.Println("结束") time.Sleep(time.Second * 5) timeout <- true }() timeout2 := make(chan bool, 2) go func() { //该goroutine如果执行时间超过了1s 那么就报告给主的goroutine //fmt.Println("结束") time.Sleep(time.Second * 1) timeout2 <- true }() select { case <-timeout: fmt.Println("超时了1") case <-timeout2: fmt.Println("超时了2") default: fmt.Println("继续下一次") } //fmt.Println(<-timeout) //for { // if timeout { // fmt.Println("结束") // break // } // time.Sleep(time.Millisecond * 10) //} //ch1 := make(chan int, 1) //ch2 := make(chan int, 1) //ch1 <- 1 //ch2 <- 2 ////随机选择一个 //select { //case data := <-ch1: // fmt.Println(data) //case data := <-ch2: // fmt.Println(data) //}}
waitgroup
package mainimport ( "fmt" "sync")//如何解决主的goroutine在子协程结束后自动结束var wg sync.WaitGroup//WaitGroup 提供了三个很有用的函数/* Add Done Wait Add的数量必须和Done的数量相等 */func f(i int) { //如果不写,fatal error: all goroutines are asleep - deadlock! //defer wg.Done() fmt.Println(i)}func main() { wg.Add(5) for i := 0; i < 5; i++ { go f(i) //go func(n int) { //一般利用defer机制 //defer wg.Done() //fmt.Println(n) //wg.Done() //}(i) } wg.Wait()}