python, java c++ 采用多线程和进程变成
多线程-每个线程占用的内存比较多,而且系统切换开销很大
1. go语言控制主的goroutine在子协程结束后结束
package main
import (
"fmt"
"sync"
)
//如何解决主的goroutine在子协程结束后自动结束
var wg sync.WaitGroup
//WaitGroup提供了三个很有用的函数
/*
Add
Done
Wait
Add的数量和Done的数量必须相等
在主的协程中使用Add, Wait, 子协城中使用Done
*/
func f(n int) {
defer wg.Done()
fmt.Println(n)
}
func main() {
wg.Add(5)
for i := 0; i < 5; i++ {
go f(i)
}
wg.Wait()
}
2 互斥锁
package main import ( "fmt" "sync" ) /* 锁 - 资源竞争 1. 按理说: 最后的结果应该是0 2. 实际的情况: 1. 不是0 2. 每次的运行结果还不一样 */ var total int var wg sync.WaitGroup var lock sync.Mutex //互斥锁, 读写锁 同步数据 能不用锁就别用锁 - 性能 //绝大多数的web系统来说 都是读多写少 //有1w个人同时读数据库 A读的时候 B能读吗? 为什么要加锁呢 一定要加锁 写上和读上面加同一把锁 //并发严重下降, B读了一个数据 造成C读了数据产生影响吗? 一定是写和读之间造成的 //如果这边锁可以做到 读之间不会产生影响, 写和读之间才会产生影响 那多好 读写锁 func add() { defer wg.Done() for i := 0; i < 100000; i++ { //先把门锁上 lock.Lock() total = 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 = total - 1 lock.Unlock() //放开锁 } } func main() { wg.Add(2) go add() go sub() wg.Wait() fmt.Println(total) }
3. 读写锁
package main import ( "fmt" "sync" "time" ) /* 锁 - 资源竞争 1. 按理说: 最后的结果应该是0 2. 实际的情况: 1. 不是0 2. 每次的运行结果还不一样 */ var total int var wg sync.WaitGroup 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 * 10) fmt.Println("修改成功") rwLock.Unlock() } func main() { wg.Add(6) for i := 0; i < 5; i++ { go read() } for i := 0; i < 1; i++ { go write() } wg.Wait() fmt.Println(total) }
4. go语言的channel,消息队列
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func consumer(queue chan int) { defer wg.Done() data := <-queue 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 msg <- 1 wg.Add(1) go consumer(msg) wg.Wait() }
以上方法只能 给一个值,取一个值,要不然就会报错
下面为 使用for取管道中的值,及关闭管道
package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup func consumer(queue chan int) { defer wg.Done() //for data := range queue{ // fmt.Println(data) //} for { data, ok := <-queue if !ok { break } else { fmt.Println(data) time.Sleep(time.Second) } } } 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 msg <- 1 wg.Add(1) go consumer(msg) msg <- 2 //关闭channel。1 已经关闭的channel不能在发送数据了,2 已经关闭的chanel消费者可以继续消费数据,值到数据取完为止 close(msg) wg.Wait() }
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) wg.Add(1) go consumer(msg) msg <- 1 //当你进行 放数据到msg中的时候 这个时候会阻塞的,阻塞之前会获取一把锁, 这把锁什么时候释放 肯定是要等到数据被消费之后 wg.Wait() //channel是多个goroutine之间线程安全, 如何保证的呢 使用锁? //如果你是没有缓冲的channel 在没有启动一个消费者之前 你放数据就会报错 //data := <- msg //fmt.Println(data) }
select语句
第一种使用场景
package main import ( "fmt" "time" ) func main() { /* go语言提供了一个select 的功能,作用于channel之上的,多路复用 select 会随机公平的选择一个case的语句进行执行 select 的使用场景, 1. timeout的超时机制 */ //第一种老的方式 //timeout := false //go func() { // // 该goroutine如果执行时间超过了1s, 那么就会报告主的goroutine // time.Sleep(time.Second * 2) // timeout = true //}() // //for { // if timeout { // fmt.Println("结束") // break // } // time.Sleep(time.Millisecond * 10) //} timeout := make(chan bool, 2) timeout2 := make(chan bool, 2) go func() { // 该goroutine如果执行时间超过了1s, 那么就会报告主的goroutine time.Sleep(time.Second * 2) timeout <- true }() go func() { // 该goroutine如果执行时间超过了1s, 那么就会报告主的goroutine time.Sleep(time.Second * 1) timeout2 <- true }() select { case <-timeout: fmt.Println("超时了") case <-timeout2: fmt.Println("超时了1") default: fmt.Println("继续下一次") } }
示例,使用协程获取机器的CPU使用率, 在6秒后自动退出
package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup var stop chan bool = make(chan bool) func cpuInfo() { defer wg.Done() for { select { case <-stop: fmt.Println("退出CPU监控") return default: time.Sleep(time.Second * 2) fmt.Println("CPU信息读取完成") } } } func main() { wg.Add(1) go cpuInfo() time.Sleep(time.Second * 6) stop <- true wg.Wait() }
使用context来完成上述监控
package main import ( "context" "fmt" "sync" "time" ) var wg sync.WaitGroup func cpuInfo(ctx context.Context) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("退出CPU监控") return default: time.Sleep(time.Second * 2) fmt.Println("CPU信息读取完成") } } } func main() { wg.Add(1) ctx, cancel := context.WithCancel(context.Background()) go cpuInfo(ctx) time.Sleep(time.Second * 6) cancel() wg.Wait() }
多个调用方式
package main import ( "context" "fmt" "sync" "time" ) var wg sync.WaitGroup func cpuInfo(ctx context.Context) { defer wg.Done() //生成一个子的context, 当父的context取消,那么父的context生成的子的context也会被取消 ctx2, _ := context.WithCancel(ctx) go memoryInfo(ctx2) //在此也可调用 //go memoryInfo(ctx) //在此也可调用 for { select { case <-ctx.Done(): fmt.Println("退出CPU监控") return default: time.Sleep(time.Second * 2) fmt.Println("CPU信息读取完成") } } } func memoryInfo(ctx context.Context) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("退出内存监控") return default: time.Sleep(time.Second * 2) fmt.Println("内存信息读取完成") } } } func main() { wg.Add(2) ctx, cancel := context.WithCancel(context.Background()) go cpuInfo(ctx) //go memoryInfo(ctx) time.Sleep(time.Second * 6) cancel() wg.Wait() }
context的超时方法
超过一定时间自动退出
package main import ( "context" "fmt" "sync" "time" ) var wg sync.WaitGroup func cpuInfo(ctx context.Context) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("退出CPU监控") return default: time.Sleep(time.Second * 2) fmt.Println("CPU信息读取完成") } } } func main() { wg.Add(1) //ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) go cpuInfo(ctx) time.Sleep(time.Second * 6) //cancel() wg.Wait() }