golang实现并发控制的方法和技巧

 更新时间:2024年03月05日 09:40:55   作者:嘻嘻爱编码  
golang 是一门支持并发的编程语言,它提供了 goroutine 和 channel 等强大的特性,让我们可以轻松地创建和管理多个执行单元,实现高效的任务处理,在本文中,我们将介绍一些 golang 的并发控制的方法和技巧,希望对你有所帮助

前言

golang 是一门支持并发的编程语言,它提供了 goroutine 和 channel 等强大的特性,让我们可以轻松地创建和管理多个执行单元,实现高效的任务处理。但是,并发也带来了一些挑战,比如如何控制 goroutine 的数量,如何避免资源竞争,如何保证数据的一致性等。在本文中,我们将介绍一些 golang 的并发控制的方法和技巧,希望对你有所帮助。

为什么要控制 goroutine 的数量

goroutine 是 golang 中最基本的执行单元,它是一种轻量级的线程,可以在一个或多个系统线程上运行。goroutine 的创建和调度都不需要进入内核,因此开销很小,我们可以轻松地创建上百万个而不会导致系统资源耗尽。那么,我们是不是可以随心所欲地使用 goroutine,而不用担心它的数量呢?

答案是否定的。虽然 goroutine 很轻量,但它也不是免费的,它也会占用一定的内存空间,每个 goroutine 至少需要 2KB 的栈空间,如果 goroutine 的数量过多,就会导致内存不足,甚至触发频繁的垃圾回收,影响程序的性能。另外,goroutine 也会消耗 CPU 的时间片,如果 goroutine 的数量超过 CPU 的核心数,就会导致上下文切换,增加 CPU 的负担。因此,我们在使用 goroutine 的时候,需要根据实际的场景和需求,合理地控制 goroutine 的数量,避免过度并发。

如何控制 goroutine 的数量

那么,我们如何控制 goroutine 的数量呢?有没有什么通用的方法或者技巧呢?其实,golang 本身就提供了一些并发控制的机制,比如 channel 和 sync 包,我们可以利用它们来实现 goroutine 的数量限制。下面,我们就来看一些具体的例子。

利用 channel 的缓冲区

channel 是 golang 中实现并发通信的重要工具,它可以在不同的 goroutine 之间传递数据,实现同步和协作。channel 有两种类型,一种是无缓冲的 channel,另一种是有缓冲的 channel。无缓冲的 channel 是同步的,发送和接收操作必须同时发生,否则会阻塞。有缓冲的 channel 是异步的,它有一个固定大小的缓冲区,可以存储一定数量的数据,发送操作只有在缓冲区满的时候才会阻塞,接收操作只有在缓冲区空的时候才会阻塞。

我们可以利用有缓冲的 channel 的特性,来实现 goroutine 的数量限制。具体的思路是,我们创建一个有缓冲的 channel,缓冲区的大小就是我们想要限制的 goroutine 的数量。然后,我们在启动一个 goroutine 之前,先向 channel 发送一个空结构体,如果 channel 满了,就会阻塞,直到有其他 goroutine 退出,从 channel 接收一个空结构体,释放缓冲区。这样,我们就可以保证同时运行的 goroutine 的数量不会超过 channel 的缓冲区大小。下面是一个简单的例子:

package main
 
import (
    "fmt"
    "sync"
    "time"
)
 
func main() {
    var wg sync.WaitGroup
    ch := make(chan struct{}, 3) // 创建一个缓冲区大小为 3 的 channel
    for i := 0; i < 10; i++ {
        ch <- struct{}{} // 向 channel 发送一个空结构体,如果 channel 满了,就会阻塞
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Println(i) // 做一些业务逻辑处理
            time.Sleep(time.Second)
            <-ch // 从 channel 接收一个空结构体,释放缓冲区
        }(i)
    }
    wg.Wait()
}

运行结果如下:

0
1
2
3
4
5
6
7
8
9

从结果中可以看到,每秒钟只并发执行了 3 个 goroutine,达到了我们的目的。这种方法的优点是简单易用,缺点是需要手动管理 channel 的发送和接收,如果忘记了,就会导致 goroutine 泄露或者死锁。

利用 sync 包

sync 包是 golang 提供的一个并发同步的包,它提供了一些常用的同步原语,比如互斥锁,条件变量,等待组等。其中,等待组(WaitGroup)是一个非常有用的工具,它可以用来等待一组 goroutine 的完成。WaitGroup 对象内部有一个计数器,最初从 0 开始,它有三个方法:Add,Done,Wait。Add 方法用来增加计数器的值,Done 方法用来减少计数器的值,Wait 方法用来阻塞,直到计数器的值为 0。

我们可以利用 WaitGroup 来实现 goroutine 的数量限制。具体的思路是,我们创建一个 WaitGroup 对象,然后在启动一个 goroutine 之前,先调用 Add 方法,增加计数器的值,如果计数器的值达到了我们想要限制的 goroutine 的数量,就会阻塞,直到有其他 goroutine 结束,调用 Done 方法,减少计数器的值,解除阻塞。这样,我们就可以保证同时运行的 goroutine 的数量不会超过我们设定的值。下面是一个简单的例子:

package main
 
import (
    "fmt"
    "sync"
    "time"
)
 
func main() {
    var wg sync.WaitGroup
    limit := 3 // 限制 goroutine 的数量为 3
    for i := 0; i < 10; i++ {
        wg.Add(1) // 增加计数器的值,如果计数器的值达到 limit,就会阻塞
        go func(i int) {
            defer wg.Done()
            fmt.Println(i) // 做一些业务逻辑处理
            time.Sleep(time.Second)
        }(i)
        if i >= limit {
            wg.Wait() // 等待其他 goroutine 结束,减少计数器的值,解除阻塞
        }
    }
    wg.Wait()
}

运行结果如下:

0
1
2
3
4
5
6
7
8
9

从结果中可以看到,每秒钟只并发执行了 3 个 goroutine,达到了我们的目的。这种方法的优点是不需要额外的 channel,缺点是需要手动管理 WaitGroup 的 Add 和 Done 方法,如果忘记了,也会导致 goroutine 泄露或者死锁。

总结

在本文中,我们介绍了为什么要控制 goroutine 的数量,以及如何使用 golang 的 channel 和 sync 包来实现 goroutine 的数量限制。这些方法都是基于 golang 的并发特性,不需要引入第三方的库或者框架,可以方便地应用在实际的项目中。当然,这些方法并不是唯一的,也不一定是最优的,你可以根据你的具体的场景和需求,选择合适的方法,或者自己设计更好的方法,来实现 goroutine 的数量限制。

以上就是golang实现并发控制的方法和技巧的详细内容,更多关于golang并发控制的资料请关注脚本之家其它相关文章!

相关文章

  • go语言题解LeetCode66加一示例详解

    go语言题解LeetCode66加一示例详解

    这篇文章主要为大家介绍了go语言题解LeetCode66加一示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • golang switch语句的灵活写法介绍

    golang switch语句的灵活写法介绍

    这篇文章主要介绍了golang switch语句的灵活写法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • GoLang使goroutine停止的五种方法实例

    GoLang使goroutine停止的五种方法实例

    goroutine是Go并行设计的核心,下面这篇文章主要给大家介绍了关于GoLang使goroutine停止的五种方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Go Build编译打包文件的完整步骤

    Go Build编译打包文件的完整步骤

    go build命令是用于编译Go语言程序并生成可执行文件,它可以将Go源代码编译成机器代码,并将其打包成可执行文件,方便在不同的操作系统上运行,这篇文章主要给大家介绍了关于Go Build编译打包文件的完整步骤,需要的朋友可以参考下
    2024-02-02
  • 简单易用的Go逗号comma ok模式使用详解

    简单易用的Go逗号comma ok模式使用详解

    这篇文章主要为大家介绍了简单易用的Go逗号comma ok模式使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • GoFrame框架ORM原生方法对象操作开箱体验

    GoFrame框架ORM原生方法对象操作开箱体验

    这篇文章主要为大家介绍了GoFrame框架ORM原生方法对象操作的开箱体验,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 详解Go语言中ErrGroup的使用

    详解Go语言中ErrGroup的使用

    本文主要为大家详细介绍了Go语言中errGroup的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2023-07-07
  • 让go程序以后台进程或daemon方式运行方法探究

    让go程序以后台进程或daemon方式运行方法探究

    本文探讨了如何通过Go代码实现在后台运行的程序,最近我用Go语言开发了一个WebSocket服务,我希望它能在后台运行,并在异常退出时自动重新启动,我的整体思路是将程序转为后台进程,也就是守护进程(daemon)
    2024-01-01
  • golang不到30行代码实现依赖注入的方法

    golang不到30行代码实现依赖注入的方法

    这篇文章主要介绍了golang不到30行代码实现依赖注入的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • Golang分布式应用之Redis示例详解

    Golang分布式应用之Redis示例详解

    这篇文章主要为大家介绍了Golang分布式应用之Redis示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07

最新评论