Golang并发模型:何时该用Goroutine?何时该用Channel?

Golang并发模型详解

Goroutine最佳实践总是考虑Goroutine的退出机制,使用sync.WaitGroup来等待Goroutine完成,避免创建无限制的Goroutine(考虑工作池模式),为Goroutine添加恢复机制(defer + recover)

引言

  • 简要介绍Golang的并发模型
  • Goroutine和Channel的基本概念
  • 为什么需要理解它们的使用时机

第一部分:Goroutine的使用场景

1. 执行独立任务时
// 示例:并发执行多个独立HTTP请求
func fetchURL(url string, wg *sync.WaitGroup){
    defer wg.Done()
    resp, err := http.Get(url)
    if err !=nil{
        fmt.Printf("Error fetching %s: %v\n", url, err)
        return
    }
    fmt.Printf("Fetched %s, status: %s\n", url, resp.Status)
}

func main(){
    urls :=[]string{"https://golang.org","https://google.com","https://github.com"}
var wg sync.WaitGroup

    for_, url :=range urls {
        wg.Add(1)
        go fetchURL(url,&wg)
    }

    wg.Wait()
    fmt.Println("All requests completed!")
}
2. 需要并行计算时
// 示例:并行计算斐波那契数列
func calculateFibonacci(n int, result chan<-int){
    if n <=1{
        result <- n
        return
    }

    a :=make(chanint)
    b :=make(chanint)

    go calculateFibonacci(n-1, a)
    go calculateFibonacci(n-2, b)

    result <-(<-a +<-b)
}

func main(){
    result :=make(chanint)
    go calculateFibonacci(10, result)
    fmt.Printf("Fib(10) = %d\n",<-result)
}
3. 处理I/O密集型操作时
// 示例:并发读取多个文件
func readFile(filename string, results chan<-string){
    data, err := os.ReadFile(filename)
    if err !=nil{
        results <- fmt.Sprintf("Error reading %s: %v", filename, err)
        return
    }
    results <- fmt.Sprintf("Read %s: %d bytes", filename,len(data))
}

func main(){
    files :=[]string{"file1.txt","file2.txt","file3.txt"}
    results :=make(chanstring,len(files))

    for_, file :=range files {
        go readFile(file, results)
    }

    for range files {
        fmt.Println(<-results)
    }
}
4. 实现后台任务时
// 示例:后台日志处理器
func logProcessor(logs <-chanstring){
    for logEntry :=range logs {
    // 模拟处理延迟
        time.Sleep(100* time.Millisecond)
        fmt.Printf("Processed log: %s\n", logEntry)
    }
}

func main(){
    logChan :=make(chanstring,100)
    go logProcessor(logChan)

    // 模拟生成日志
    for i :=0; i <10; i++{
        logChan <- fmt.Sprintf("Log entry %d", i)
    }

    close(logChan)
    time.Sleep(1* time.Second)// 等待处理完成
}

第二部分:Channel的使用场景

1. Goroutine间通信时
// 示例:生产者-消费者模型
func producer(items chan<-int){
    for i :=0; i <5; i++{
        items <- i
        time.Sleep(time.Second)
    }
    close(items)
}

func consumer(id int, items <-chanint){
    for item :=range items {
        fmt.Printf("Consumer %d received: %d\n", id, item)
    }
}

func main(){
    items :=make(chanint)

    go producer(items)

    // 启动多个消费者
    for i :=1; i <=3; i++{
        go consumer(i, items)
    }

    time.Sleep(6* time.Second)
}
2. 同步Goroutine执行时
// 示例:使用Channel同步多个Goroutine
func worker(id int, ready <-chanstruct{}, done chan<-int){
    <-ready // 等待开始信号
    fmt.Printf("Worker %d started\n", id)
    time.Sleep(time.Duration(rand.Intn(3))* time.Second)
    fmt.Printf("Worker %d finished\n", id)
    done <- id
}

func main(){
    const numWorkers =5
    ready :=make(chanstruct{})
    done :=make(chanint, numWorkers)

    // 启动workers
    for i :=1; i <= numWorkers; i++{
    go worker(i, ready, done)
}

// 同时开始所有workers
close(ready)

// 等待所有workers完成
for i :=1; i <= numWorkers; i++{
    <-done
}

    fmt.Println("All workers completed")
}
3. 实现管道模式时
// 示例:数据处理管道
func stage1(in <-chan int)<-chan int{
    out :=make(chan int)
    go func(){
    for n :=range in {
            out <- n *2
    }
    close(out)
}()
    return out
}

func stage2(in <-chan int)<-chan int{
    out :=make(chan int)
    go func(){
        for n :=range in {
            out <- n +1
        }
        close(out)
    }()
    return out
}

func stage3(in <-chan int)<-chan string{
    out :=make(chan string)
    go func(){
        for n :=range in {
            out <- fmt.Sprintf("Result: %d", n)
        }
    close(out)
    }()
    return out
}

func main(){
    // 创建输入channel
    in :=make(chanint)

    // 构建管道
    pipeline :=stage3(stage2(stage1(in)))

    // 发送数据
    go func(){
        for i :=0; i <5; i++{
            in <- i
        }
        close(in)
    }()

    // 接收结果
    for result :=range pipeline {
        fmt.Println(result)
    }
}
4. 限制并发数量时
// 示例:使用缓冲Channel实现工作池
func worker(id int, jobs <-chan int, results chan<-int){
    for j :=range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        time.Sleep(time.Second)
        fmt.Printf("Worker %d finished job %d\n", id, j)
        results <- j *2
    }
}

func main(){
    const numJobs =10
    const numWorkers =3

    jobs :=make(chanint, numJobs)
    results :=make(chanint, numJobs)

    // 启动workers
    for w :=1; w <= numWorkers; w++{
        go worker(w, jobs, results)
    }

    // 发送jobs
    for j :=1; j <= numJobs; j++{
        jobs <- j
    }
    close(jobs)

    // 收集结果
    for a :=1; a <= numJobs; a++{
        <-results
    }
}

第三部分:Goroutine和Channel的联合使用

1. 扇出模式
// 示例:一个生产者,多个消费者
func producer(nums chan<-int){
    for i :=0; i <10; i++{
        nums <- i
    }
    close(nums)
}

func consumer(id int, nums <-chan int, done chan<-bool){
    for num :=range nums {
        fmt.Printf("Consumer %d got %d\n", id, num)
        time.Sleep(time.Duration(rand.Intn(500))* time.Millisecond)
    }
    done <-true
}

func main(){
    nums :=make(chanint)
    done :=make(chanbool)

    go producer(nums)

    // 启动多个消费者
    for i :=0; i <3; i++{
    go consumer(i, nums, done)
}

    // 等待所有消费者完成
    for i :=0; i <3; i++{
        <-done
    }
}
2. 扇入模式
// 示例:多个生产者,一个消费者
func producer(id int, nums chan<-int){
    for i :=0; i <3; i++{
        nums <- id*10+ i
        time.Sleep(time.Duration(rand.Intn(500))* time.Millisecond)
    }
}

func consumer(nums <-chanint, done chan<-bool){
    for num :=range nums {
        fmt.Printf("Received: %d\n", num)
    }
    done <-true
}

func main(){
    nums :=make(chanint)
    done :=make(chanbool)

    // 启动多个生产者
    for i :=0; i <3; i++{
        go producer(i, nums)
    }

    // 启动消费者
    go func(){
        time.Sleep(2* time.Second)
        close(nums)
    }()
    go consumer(nums, done)

    <-done
}
3. 超时控制
// 示例:使用select实现超时控制
func longRunningTask(result chan<-string){
    // 模拟长时间运行的任务
    time.Sleep(3* time.Second)
    result <-"Task completed"
}

func main(){
    result :=make(chanstring)
    go longRunningTask(result)

    select{
        case res :=<-result:
            fmt.Println(res)
        case<-time.After(2* time.Second):
            fmt.Println("Task timed out")
    }
}

第四部分:最佳实践与常见陷阱

Goroutine最佳实践
  • 总是考虑Goroutine的退出机制
  • 使用sync.WaitGroup来等待Goroutine完成
  • 避免创建无限制的Goroutine(考虑工作池模式)
  • 为Goroutine添加恢复机制(defer + recover)
Channel最佳实践
  • 明确Channel的所有权(哪个Goroutine负责关闭)
  • 使用缓冲Channel来解耦生产者和消费者
  • 考虑使用context.Context来取消Channel操作
  • 小心nil Channel和已关闭Channel的操作
常见陷阱
  • Goroutine泄漏
  • Channel死锁
  • 共享内存竞争条件
  • 不正确的Channel关闭

结论

  • 总结Goroutine和Channel的核心使用场景
  • 强调根据具体需求选择合适的并发模式
  • 推荐进一步学习资源(官方文档、经典书籍等)

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值