golang学习笔记21——golang协程管理及sync.WaitGroup的使用

引言

Golang 中的协程(goroutine)为并发编程带来了极大的便利,但在实际开发中,如果对协程管理不当,也会产生一系列问题。本文将深入探讨这些问题,并结合代码示例给出相应的解决方案。

协程管理中的常见问题

1.协程泄漏

  • 协程在执行过程中,如果由于某些原因(如阻塞在某个通道上、陷入死锁等)没有正常退出,就会导致协程泄漏。大量的协程泄漏会耗尽系统资源,如内存等。
  • 示例代码:
package main

import (
    "fmt"
    "time"
)

func leakyGoroutine() {
    // 这个协程会一直阻塞,导致协程泄漏
    <-make(chan int)
}

func main() {
    for i := 0; i < 10; i++ {
        go leakyGoroutine()
    }

    // 主线程休眠一段时间,让协程有机会执行
    time.Sleep(5 * time.Second)
    fmt.Println("程序结束,但协程泄漏了")
}

2.协程过多导致资源耗尽

  • 创建过多的协程而没有进行有效的限制和管理,会使系统资源(如 CPU 时间片、内存等)被大量占用,从而影响系统的性能和稳定性。
  • 示例代码:
package main

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

func manyGoroutines() {
    var wg sync.WaitGroup
    for i := 0; i < 100000; i++ {
        wg.Add(1)
        go func() {
            // 模拟协程执行一些简单的操作
            for j := 0; j < 1000; j++ {
                _ = j
            }
            wg.Done()
        }()
    }
    wg.Wait()
}

func main() {
    before := runtime.NumGoroutine()
    manyGoroutines()
    after := runtime.NumGoroutine()
    fmt.Printf("创建前协程数量: %d, 创建后协程数量: %d\n", before, after)
}

解决方案

1.避免协程泄漏

  • 合理使用通道和超时机制
    • 对于可能阻塞的通道操作,可以设置超时时间,避免协程无限制地等待。
  • 代码示例:
package main

import (
    "fmt"
    "time"
)

func nonLeakyGoroutine() {
    // 创建一个带超时的通道
    timeout := time.After(3 * time.Second)
    ch := make(chan int)

    go func() {
        // 模拟可能阻塞的操作
        time.Sleep(5 * time.Second)
        ch <- 1
    }()

    select {
    case <-ch:
        fmt.Println("协程正常接收数据")
    case <-timeout:
        fmt.Println("操作超时,协程退出")
    }
}

func main() {
    for i := 0; i < 10; i++ {
        go nonLeakyGoroutine()
    }

    // 主线程休眠一段时间
    time.Sleep(5 * time.Second)
    fmt.Println("程序结束,没有协程泄漏")
}
  • 避免死锁
    • 在多个协程之间进行同步和通信时,要确保资源的获取和释放顺序正确,避免出现死锁导致协程无法退出。

2.限制协程数量

  • 使用信号量(Semaphore)
    • 通过信号量来限制同时执行的协程数量。
  • 代码示例:
package main

import (
    "fmt"
    "sync"
)

// 定义信号量
var semaphore = make(chan struct{}, 10)

func limitedGoroutine() {
    // 获取信号量
    semaphore <- struct{}{}
    defer func() {
        // 释放信号量
        <-semaphore
    }()

    // 协程执行的操作
    fmt.Println("协程执行中...")
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            limitedGoroutine()
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println("所有协程执行完毕")
}

总结

在 Go 语言中,协程管理是并发编程的关键部分。通过避免协程泄漏和合理限制协程数量等措施,可以有效地提高程序的性能和稳定性,充分发挥 Go 语言在并发编程方面的优势。

关注我看更多有意思的文章哦!👉👉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值