Golang 在 Linux 平台上的并发控制

Go 语言以其出色的并发模型而闻名,在 Linux 平台上提供了多种并发控制机制。下面将详细介绍主要的并发控制方法。

1. Goroutine 基础

Goroutine 是 Go 的轻量级线程,由 Go 运行时管理。使用 go 关键字即可启动一个新的 Goroutine。示例代码如下:

go 复制代码
go func() { 
    // 并发执行的代码
}()

2. Channel 同步

Channel 是 Go 的核心并发原语,用于在不同的 Goroutine 之间进行通信。以下是一个简单的示例,展示了如何使用 Channel 进行数据的发送和接收:

go 复制代码
ch := make(chan int)
// 发送数据
go func() { 
    ch <- 42
}()
// 接收数据
value := <-ch

3. WaitGroup 等待组

sync.WaitGroup 用于等待一组 Goroutine 完成。示例代码如下:

go 复制代码
var wg sync.WaitGroup
for i := 0; i < 5; i++ { 
    wg.Add(1) 
    go func(i int) { 
        defer wg.Done() 
        fmt.Println(i) 
    }(i)
}
wg.Wait() // 等待所有 Goroutine 完成

4. Mutex 互斥锁

sync.Mutex 用于保护共享资源,避免多个 Goroutine 同时访问而导致数据竞争。示例代码如下:

go 复制代码
var ( 
    counter int 
    mu sync.Mutex
)
func increment() { 
    mu.Lock() 
    defer mu.Unlock() 
    counter++
}

5. RWMutex 读写锁

sync.RWMutex 提供了更高效的读写控制,允许多个 Goroutine 同时进行读操作,但在写操作时会互斥。示例代码如下:

go 复制代码
var ( 
    data map[string]string 
    rwMux sync.RWMutex
)
// 读操作
func read(key string) string { 
    rwMux.RLock() 
    defer rwMux.RUnlock() 
    return data[key]
}
// 写操作
func write(key, value string) { 
    rwMux.Lock() 
    defer rwMux.Unlock() 
    data[key] = value
}

6. Once 单次执行

sync.Once 确保某操作只执行一次。示例代码如下:

go 复制代码
var once sync.Once
func setup() { 
    once.Do(func() { 
        // 只执行一次的初始化代码 
    })
}

7. Context 上下文控制

context 包用于控制 Goroutine 的生命周期。示例代码如下:

go 复制代码
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) { 
    select { 
    case <-ctx.Done(): 
        return // 被取消 
    case <-time.After(time.Second): 
        fmt.Println("工作完成") 
    }
}(ctx)
// 取消操作
cancel()

8. Select 多路复用

select 语句用于监听多个 Channel。示例代码如下:

go 复制代码
select {
case msg1 := <-ch1: 
    fmt.Println(msg1)
case msg2 := <-ch2: 
    fmt.Println(msg2)
case <-time.After(time.Second): 
    fmt.Println("超时")
}

9. 原子操作

sync/atomic 包提供原子操作。示例代码如下:

go 复制代码
var count int32
atomic.AddInt32(&count, 1) // 原子增加

10. Worker Pool 模式

使用缓冲 Channel 实现工作池。示例代码如下:

go 复制代码
func worker(id int, jobs <-chan int, results chan<- int) { 
    for j := range jobs { 
        fmt.Println("worker", id, "processing job", j) 
        results <- j * 2 
    }
}
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动 3 个 worker
for w := 1; w <= 3; w++ { 
    go worker(w, jobs, results)
}
// 发送 9 个任务
for j := 1; j <= 9; j++ { 
    jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 9; a++ { 
    <-results
}

性能考虑

  • 在 Linux 上,Goroutine 调度器会利用多核 CPU。
  • 避免过度使用锁,优先考虑使用 Channel 进行通信。
  • 可以使用 runtime.GOMAXPROCS() 控制使用的 CPU 核心数。

调试工具

  • go tool trace:用于可视化并发执行情况。
  • pprof:用于性能分析。
  • -race 标志:用于检测数据竞争。

Go 的并发模型在 Linux 平台上表现优异,合理使用这些并发控制机制可以构建高效可靠的并发程序。

相关推荐
天若有情6733 小时前
程序员原创|借鉴JS事件冒泡,根治电脑文件混乱的“冒泡整理法”
开发语言·javascript·windows·ecmascript·电脑·办公·日常
特种加菲猫4 小时前
继承,一场跨越时空的对话
开发语言·c++
小码哥_常5 小时前
告别MySQL!大厂集体转投PostgreSQL,到底藏着什么玄机?
后端
玩转单片机与嵌入式5 小时前
玩转边缘AI(TInyML):需要掌握的C++知识汇总!
开发语言·c++·人工智能
茉莉玫瑰花茶5 小时前
Qt 信号与槽 [ 1 ]
开发语言·数据库·qt
刀法如飞6 小时前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
swipe6 小时前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp6 小时前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端