golang 并发 goroutine sync.Lock atomic WaitGroup 协程通信(共享数据,channqel消息)channel

概念

  • 并发 某个时间段内同时执行。比如单行CPU通过切换执行不同的应用来实现并发。
  • 并行 某个时间点同时执行。单核CPU不可能实现并行。

查看当前程序的并发数量

go 复制代码
package main

import "fmt"
import "runtime"
import "time"
func main(){
     // 获取【逻辑CPU核心数】(含超线程)
     // 4核8线程 → 返回8
     var nums = runtime.NumCPU()
     fmt.Println("线程数量",nums)

     // 获取当前系统:linux / windows / darwin
     var sysname = runtime.GOOS
     fmt.Println("系统",sysname)

     // 获取Go最大并行GOMAXPROCS(默认=CPU核心数)
     // 代表:最多同时跑多少个工作线程
     var maxThreads = runtime.GOMAXPROCS(0)
     fmt.Println("最大可用线程数量", maxThreads)


     // 当前goroutine数量(初始只有main)
     var goroutineNum = runtime.NumGoroutine()
     fmt.Println("goroutineNum",goroutineNum) // 1

     // 启动2个goroutine
     go Sleep1()
     go Sleep1()

     // 刚用go关键字,立刻计数+2 → 结果=3
     goroutineNum = runtime.NumGoroutine()
     fmt.Println("goroutineNum",goroutineNum) // 3

     // 打印所有goroutine的栈信息
     buf := make([]byte, 1<<20) // 分配1MB缓冲区
     length := runtime.Stack(buf, true) // 获取全部goroutine栈
     stackInfo := string(buf[:length])

     fmt.Println("======== 全部栈信息 ========")
     fmt.Println(stackInfo)
     fmt.Println("============================")

     // 主线程长时间休眠,不让程序退出
     time.Sleep(time.Second * 20000)
}

// 睡眠10秒的goroutine
func Sleep1 (){
     time.Sleep(time.Second * 10)
}

修改线程数量

css 复制代码
package main

import "fmt"
import "runtime"

func main() {
     // 参数是0表示读取
     a := runtime.GOMAXPROCS(0)
     fmt.Println(a)

     // 设置2个线程
     runtime.GOMAXPROCS(2)
     a = runtime.GOMAXPROCS(0)
     fmt.Println(a)

     // 虽然可以设置多个线程,但是不推荐,用默认的就好
     runtime.GOMAXPROCS(100)
     a = runtime.GOMAXPROCS(0)
     fmt.Println(a)
}

goroutine的使用

go 复制代码
package main

import "fmt"
import "time"

func SayHello(index int) {
     for {
        fmt.Println("Hello", index)
        time.Sleep(time.Second)
     }
}

func main() {

     go SayHello(1)
     go SayHello(2)

     go func(index int) {
        for {
         fmt.Println("main hello ", index)
         time.Sleep(time.Second)
        }
     }(3)
     var input int
     fmt.Scanln(&input)
}

atomic

dart 复制代码
var num atomic.Uint64 
num.Store(10) // 存值 
v := num.Load() // 取值 
num.Add(1) // 原子+1

sync.WaitGroup

  • Add(n)开启了n个协程
  • Done()计数器减1
  • Wait()等待所有的协程结束
go 复制代码
package main

import "fmt"
import "sync"
import "time"

func SayHello(tag string,wg *sync.WaitGroup){

     defer func(){
        wg.Done()
     }()
     for i:=0;i<10;i++{
        fmt.Println(tag,"hello")
        time.Sleep(time.Second)
     }
}

func main(){
     wg :=  sync.WaitGroup{}

     wg.Add(2)
     go SayHello("Coroutine1", &wg)
     go SayHello("Coroutine2", &wg)

     //等待协程结束
     fmt.Println(time.Now().Unix())
     wg.Wait()
     fmt.Println(time.Now().Unix())
}

共享数据的通信

csharp 复制代码
package main

import (
     "fmt"
     "sync"
     "runtime"
)

var total int = 0

func Add1(lock *sync.Mutex){
     lock.Lock()
     total += 1
     lock.Unlock()
}

func main(){
     lock := &sync.Mutex{}

     for i:=0;i<20;i++{
        go Add1(lock)
     }

     for{
        lock.Lock()
        value := total
        lock.Unlock()
        fmt.Println(value)
        runtime.Gosched()
        if value > 10 {
         break
        }
     }

}

channel

go 复制代码
// chan int是一个类型
var a chan int

// 不带缓存的channel,每次写入都阻塞
var a = make(chan int)

// 带缓存的channel,性能会更好
var a = make(chan int, 10)

//写入结束,调用close关闭通道
close(a)

// 默认chan是可以写,也可以读取
var aRead <-chan int = a
var aWrite chan<- int = a
go 复制代码
package main

import "fmt"
import "time"
import "sync"

func Add1(a chan int, wg *sync.WaitGroup) {
     defer wg.Done()
     for value := range a {
        fmt.Println(value)
     }
}

func Sub1(a chan int, wg *sync.WaitGroup) {
     defer wg.Done()
     for i := 0; i < 5; i++ {
        a <- 100
        fmt.Println("sub1 <- 100")
        time.Sleep(time.Second)
     }
     close(a)
}

func main() {
     wg := sync.WaitGroup{}
     wg.Add(2)
     var a = make(chan int)
     go Sub1(a, &wg)

     fmt.Println("此时,channel被阻塞")
     time.Sleep(time.Second * 2)

     go Add1(a, &wg)
     wg.Wait()

}

chan的关闭

go 复制代码
package main

import "fmt"
import "time"

func Consume(c *chan int) {
        defer fmt.Println(" consume finish")
        for {
                // 这里无法判断chan是否关闭
                //content := <- *c
                //fmt.Println("content is ", content)
                content, ok := <-*c
                if ok {
                        fmt.Println("1", content, ok)
                } else {
                        // 2 0 false content是0,也可能是chan传递过来的就是0,所以必须>用ok来判断是否被关闭
                        fmt.Println("2", content, ok)
                        break
                }
        }
}

func Producer(c *chan int) {
        defer func() {
                close(*c)
                fmt.Println("producer finish")
        }()
        *c <- 100
        *c <- 200

}

func main() {
        a := make(chan int, 10)
        go Consume(&a)
        go Producer(&a)

        time.Sleep(time.Hour)

}
相关推荐
Reart1 小时前
从0解构tinyweb项目(十三)--剩余Handler自读验证(未完成版)
后端
Gopher_HBo1 小时前
接入层Nginx
后端
IT_陈寒2 小时前
Vite热更新把我整不会了,原来还要这样配!
前端·人工智能·后端
Gauss松鼠会2 小时前
GaussDB(DWS) SQL性能问题案例集
java·数据库·经验分享·spring boot·后端·sql·gaussdb
霸道流氓气质2 小时前
Spring Boot 分页查询接口设计与实现 —— 技术总结与完整示例
java·spring boot·后端
赴前尘3 小时前
Go 语言实现 TOTP 双因素认证完整指南
开发语言·后端·golang
武子康13 小时前
Java-07 深入浅出 MyBatis数据库一对多关系模型实战:表结构设计与查询实现
java·后端
花椒技术14 小时前
企业内部 Agent 落地复盘:Gateway、Skill 和二次确认如何串起受控业务执行
后端·agent·ai编程