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语言编写一个并发包,适合大部分并发任务,开箱即用,文中的示例代码讲解详细,有需要的小伙伴可以参考下
    2023-10-10
  • golang中defer的使用规则详解

    golang中defer的使用规则详解

    大家应该都知道在golang当中,defer代码块会在函数调用链表中增加一个函数调用。下面这篇文章主要给大家介绍了关于golang中defer的使用规则,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-07-07
  • 从Node.js 转到 Go平台

    从Node.js 转到 Go平台

    回顾过去的一年,我们在技术栈上的最大改变就是从 Node.js 切换到 Go 。我们的联合创始人,Steve Kaliski, 在 Poptip 把 Node.js 切换成了 Go,可惜他没有学习到当时的教训。
    2015-03-03
  • Golang 流水线设计模式实践示例详解

    Golang 流水线设计模式实践示例详解

    这篇文章主要为大家介绍了Golang 流水线设计模式实践示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 浅谈Go语言的高效编码细节

    浅谈Go语言的高效编码细节

    这篇文章主要介绍了浅谈Go语言的高效编码细节,我们都知道golang是天生的高并发,高效的编译型语言,可我们也都可知道,工具再好,用法不对,全都白费,我们来举2个常用路径来感受一下
    2023-01-01
  • Go协程的底层原理及分析

    Go协程的底层原理及分析

    文章解释了操作系统中的进程、线程和协程的概念及其区别,并详细描述了Go语言中协程的工作原理和调度机制,包括G-M-P模型、抢占式调度、基于信号的抢占式调度等
    2026-05-05
  • Go语言如何实现TCP通信详解

    Go语言如何实现TCP通信详解

    go里面实现tcp没有像之前写的C++那些那么麻烦,在C++里面要先创建套接字,然后绑定ip地址,go里面直接就一个函数建立套接字,然后在进行通信就可以了,下面这篇文章主要给大家介绍了关于Go语言如何实现TCP通信的相关资料,需要的朋友可以参考下
    2023-01-01
  • Golang使用JWT进行认证和加密的示例详解

    Golang使用JWT进行认证和加密的示例详解

    JWT是一个签名的JSON对象,通常用作Oauth2的Bearer token,JWT包括三个用.分割的部分。本文将利用JWT进行认证和加密,感兴趣的可以了解一下
    2023-02-02
  • go-micro微服务JWT跨域认证问题

    go-micro微服务JWT跨域认证问题

    JWT 以 JSON 对象的形式安全传递信息。因为存在数字签名,因此所传递的信息是安全的,这篇文章主要介绍了go-micro微服务JWT跨域认证,需要的朋友可以参考下
    2023-01-01
  • Golang gin跨域解决方案示例

    Golang gin跨域解决方案示例

    这篇文章主要为大家介绍了Golang gin跨域解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04

最新评论