发布于2026-07-28 阅读(0)
扫一扫,手机访问
在 CentOS 上搞 Golang 并发编程,其实核心就一句话:用好 goroutine、channel 和 sync 包,就能把并发任务安排得明明白白。Golang 的“原生并发”不是噱头,而是实实在在的轻量级线程调度机制,让多线程开发变得像单线程一样简单。下面就从环境配置到实战技巧,把这一套东西彻底讲透。

在 CentOS 上跑 Go 程序,先得把环境搭好。两种方式任选:
wget https://golang.org/dl/go1.20.4.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.20.4.linux-amd64.tar.gz
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
source ~/.bashrc
go version,看到版本号就算成了。Goroutine 是 Golang 并发的基础,用 go 关键字就能启动。它由 Go 运行时管理,内存占用只有 2KB 左右,比传统线程轻量太多。举个例子,启动 5 个 goroutine 分别打印编号:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Goroutine %d executed\n", id)
}(i)
}
wg.Wait() // 等待所有 goroutine 完成
}
关键点:用 sync.WaitGroup 来同步 goroutine,否则主程序可能提前跑完,goroutine 还没执行就退出了。
Channel 是 goroutine 之间传递数据的“管道”,能避免共享内存带来的竞态条件。来看一个带缓冲 channel 实现并发下载任务的例子:
package main
import (
"fmt"
"net/http"
)
func download(url string, ch chan<- string) {
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("Error downloading %s: %v", url, err)
return
}
defer resp.Body.Close()
ch <- fmt.Sprintf("Downloaded %s (Status: %s)", url, resp.Status)
}
func main() {
urls := []string{"https://example.com/file1", "https://example.com/file2"}
ch := make(chan string, len(urls)) // 带缓冲 channel
for _, url := range urls {
go download(url, ch)
}
for i := 0; i < len(urls); i++ {
fmt.Println(<-ch) // 接收并打印结果
}
}
关键点:带缓冲 channel 可以避免发送操作阻塞,提高并发效率。
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final counter:", counter) // 输出 1000
}
关键点:读写操作必须用 Lock()/Unlock() 包裹,保证原子性。
对于 CPU 密集型任务,如果 goroutine 开太多,资源反而会耗尽。工作池模式正好解决这个问题。来看一个例子:创建 3 个 worker 处理 5 个任务。
package main
import "fmt"
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, j)
results <- j * 2 // 模拟任务处理
}
}
func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
// 启动 3 个 worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送任务
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// 接收结果
for a := 1; a <= 5; a++ {
fmt.Println("Result:", <-results)
}
}
关键点:通过带缓冲的 jobs 通道控制任务队列,results 通道收集结果,worker 数量就是并发上限。
Context 用来传递取消信号、超时或截止时间,避免 goroutine 泄漏。下面是一个用 context 取消长时间运行任务的例子:
package main
import (
"context"
"fmt"
"time"
)
func longTask(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Task %d cancelled\n", id)
return
default:
fmt.Printf("Task %d running...\n", id)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 释放资源
go longTask(ctx, 1)
time.Sleep(3 * time.Second) // 主程序等待
}
关键点:WithTimeout 设置 2 秒超时,超时后 ctx.Done() 触发,goroutine 优雅退出。
-race 标志就能用:go run -race main.go
测试时也可以:
go test -race ./...
sync/atomic 包性能更好:package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var counter int32
for i := 0; i < 1000; i++ {
go func() {
atomic.AddInt32(&counter, 1)
}()
}
time.Sleep(time.Second)
fmt.Println("Counter:", atomic.LoadInt32(&counter)) // 输出 1000
}
GOMAXPROCS 设置并行度(默认是 CPU 核心数),根据实际场景调整。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8