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语言函数学习教程

    Go语言函数学习教程

    这篇文章主要介绍了Go语言函数基本用法,结合实例形式分析了Go语言函数的格式、定义、使用方法与相关注意事项,需要的朋友可以参考下
    2016-07-07
  • Go语言中的init函数特点及用法详解

    Go语言中的init函数特点及用法详解

    在Go语言中,init()函数是一种特殊的函数,用于在程序启动时自动执行一次。它的存在为我们提供了一种机制,可以在程序启动时进行一些必要的初始化操作,为程序的正常运行做好准备,在这篇文章中,我们将详细探讨init()函数的特点、用途和注意事项
    2023-06-06
  • 浅谈Go切片的值修改是否会覆盖数组的值 

    浅谈Go切片的值修改是否会覆盖数组的值 

    本文主要介绍了浅谈Go切片的值修改是否会覆盖数组的值,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 
    2022-02-02
  • 完美解决golang go get私有仓库的问题

    完美解决golang go get私有仓库的问题

    这篇文章主要介绍了完美解决golang go get私有仓库的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Golang关键字defer的用法详解

    Golang关键字defer的用法详解

    defer是Go里面的一个关键字,用在方法或函数前面,作为方法或函数的延迟调用。这篇文章主要为大家介绍了defer的简单使用,需要的可以参考一下
    2023-05-05
  • Go语言atomic.Value如何不加锁保证数据线程安全?

    Go语言atomic.Value如何不加锁保证数据线程安全?

    这篇文章主要介绍了Go语言atomic.Value如何不加锁保证数据线程安全详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • golang实现大文件上传功能全过程

    golang实现大文件上传功能全过程

    Go语言可以用来实现大文件传输,下面这篇文章主要给大家介绍了关于golang实现大文件上传功能的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Go Singleflight导致死锁问题解决分析

    Go Singleflight导致死锁问题解决分析

    这篇文章主要为大家介绍了Go Singleflight导致死锁问题解决分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Golang如何交叉编译各个平台的二进制文件详解

    Golang如何交叉编译各个平台的二进制文件详解

    这篇文章主要给大家介绍了关于Golang如何交叉编译各个平台的二进制文件的相关资料,并介绍了golang如何让编译生产的二进制文件变小,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • 一文带你深入了解Golang中的参数传递机制

    一文带你深入了解Golang中的参数传递机制

    值传递和引用传递是编程语言中两种主要的参数传递方式,决定了函数调用过程中实参如何影响形参以及函数内部对形参的修改是否会影响到原始实参,下面就跟随小编一起深入了解下golang中参数传递机制吧
    2024-01-01

最新评论