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)

}
相关推荐
陈随易5 小时前
在 VSCode 里,把项目一键部署到服务器
前端·后端·程序员
鱼樱前端6 小时前
我用 Claude Code 后,编码效率翻 3 倍。但更值钱的是别的。
前端·后端·ai编程
犀利豆6 小时前
Claude code tools 研究系列-开篇(AskUserQuestion)
后端
颜酱9 小时前
06 | 把 meta_config 同步进 MySQL(生成阶段)
前端·人工智能·后端
IT_陈寒10 小时前
SpringBoot自动配置坑了我一周,原来问题这么蠢!
前端·人工智能·后端
iOS开发上架哦11 小时前
Android代码混淆与iOS加固技术详解
后端·ios
Alan_69112 小时前
商品详情优化三板斧-拆分-多级缓存-GC调参
后端·缓存
Conan在掘金12 小时前
ArkTS 进阶之道(3):为哈禁解构声明?类型一眼可见 vs 推断链断裂
后端
晚安code12 小时前
干掉成山的 if-else:工厂造、策略选,一文讲透两个模式的配合
后端·设计模式
feng尘12 小时前
深度解析布隆过滤器(Bloom Filter):原理、优缺点与 1000 万黑名单实战
后端·面试