package main
import (
"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 main
import (
"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 main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func 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 main
import (
"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 main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func 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 main
import (
"fmt"
"sync"
)
/*
锁 - 资源竞争
1.按理说 最后的结果是0
2.实际的情况 1. 不是0 2.每次运行的结果不一样
*/
var total int
var wg sync.WaitGroup
//互斥锁(同步问题) - 读写锁 同步数据 能不用锁就别用锁 - 性能下降
//绝大多数的web系统来说 都是读多写少
//有1w个人同时读数据库 A读的时候 B能读么? 为什么要加锁呢?100 - 1w - 100
//立马我下单了 - 支付(会重新查一遍数据库 1w)
//一定要加锁 写 和 读上面要加同一把锁 并发严重下降。
//B读了一个数据,造成C读了数据产生影响吗? 一定是写和读之间造成的
//如果这把锁之间可以做到 读之间不会产生影响 读写之间才会产生影响 那多好 读写锁
var lock sync.Mutex
func 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 main
import (
"fmt"
"sync"
"time"
)
/*
锁 - 资源竞争
1.按理说 最后的结果是0
2.实际的情况 1. 不是0 2.每次运行的结果不一样
*/
var total int
var wg sync.WaitGroup
var 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 main
import (
"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 main
import (
"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()
}