Golang Channels 的缓冲与非缓冲通道使用场景比较

1. 概述

Channels 是 Golang 并发编程中最重要的组件之一,它是用来在协程之间传递数据的通道。在 Golang 中,有缓冲和非缓冲两种类型的 channels,本文将从使用场景、性能、特点等方面来比较这两种 channels 的优缺点。

2. 非缓冲 channels (make(chan int))

2.1 使用场景

非缓冲 channels 指的是在创建 channels 的时候不指定缓冲区大小。在非缓冲 channels 中,发送操作将会被阻塞直到有一个接收者准备好接收数据,反之亦然。这种 channels 的使用场景比较明确,主要应用在同步数据交换的场景中,例如:

等待协程完成:例如等待任务执行完成后再继续执行下一步操作;

对于多个协程的同步:例如协程 A 将数据写入 channel,协程 B 将收到该数据后再继续执行后续操作。

这样的使用方式非常适合实现消费者-生产者模型。

2.2 特点

非缓冲 channels 的特点主要有:

非缓冲 channels 不需要缓冲区,因此不会浪费内存资源;

非缓冲 channels 可以保证同步;

非缓冲 channels 的操作会阻塞当前的协程,需要等待其他协程进行对应操作。

2.3 示例代码

下面是一个使用非缓冲 channels 的示例代码,一个协程会等待另一个协程完成任务后才会继续执行。

package main

import "fmt"

func compute(ch chan int) {

fmt.Println("Start compute")

ch <- 1

fmt.Println("Finish compute")

}

func main() {

ch := make(chan int)

go compute(ch)

fmt.Println("Start main")

<-ch

fmt.Println("Finish main")

}

输出如下:

Start compute

Start main

Finish compute

Finish main

3. 缓冲 channels (make(chan int, 1))

3.1 使用场景

缓冲 channels 是指在创建 channels 的时候预先指定缓冲区大小。在缓冲 channels 中,发送操作会一直进行下去,直到缓冲区满了才会被阻塞。

因此,缓冲 channels 主要适用于异步任务的场景,例如:

异步发送数据;

缓存数据等。

3.2 特点

缓冲 channels 的特点主要有:

一开始会为 channel 分配内存缓冲区,因此可以存储一定数量的元素;

发送操作即使没有接收者也不会被阻塞,只有缓冲区满了才会阻塞;

支持异步通信。

3.3 示例代码

下面是一个使用缓冲 channels 的示例代码,一个协程会异步发送数据到 channel 中。

package main

import "fmt"

func send(ch chan int) {

fmt.Println("Start send")

ch <- 1

ch <- 2

fmt.Println("Finish send")

}

func main() {

ch := make(chan int, 1)

go send(ch)

fmt.Println("Start main")

fmt.Println(<-ch)

fmt.Println(<-ch)

fmt.Println("Finish main")

}

输出如下:

Start send

Start main

Finish send

1

2

Finish main

4. 性能比较

在 Golang 中,非缓冲 channels 是同步阻塞式的通讯机制,而缓冲 channels 则是异步非阻塞式的通讯机制。因此,从性能上来讲,缓冲 channels 的性能一般比非缓冲 channels 更好,因为缓冲 channels 能更好地利用系统资源。

下面是一个简单的基准测试,用来比较缓冲和非缓冲 channels 的性能:

package main

import (

"fmt"

"testing"

"time"

)

// 非缓冲 channels

func TestUnbufferedChannel(t *testing.T) {

ch := make(chan int)

go func() {

time.Sleep(1 * time.Second)

ch <- 1

}()

<-ch

}

// 缓冲 channels

func TestBufferedChannel(t *testing.T) {

ch := make(chan int, 1)

go func() {

time.Sleep(1 * time.Second)

ch <- 1

}()

<-ch

}

func main() {

start := time.Now()

TestUnbufferedChannel(nil)

duration := time.Since(start)

fmt.Printf("UnbufferedChannel took %v seconds\n", duration.Seconds())

start = time.Now()

TestBufferedChannel(nil)

duration = time.Since(start)

fmt.Printf("BufferedChannel took %v seconds\n", duration.Seconds())

}

输出结果如下:

UnbufferedChannel took 1.001110547 seconds

BufferedChannel took 1.00025392 seconds

可以看出,缓冲 channels 和非缓冲 channels 的性能差异不大,在实用中缓冲 channels 的性能一般会更优越。

5. 总结

本文分析了 Golang 中非缓冲 channels 和缓冲 channels 的使用场景、特点以及比较了它们的性能。从使用场景来看,非缓冲 channels 主要适用于同步数据交换的场景,缓冲 channels 则主要适用于异步任务的场景。从特点来看,非缓冲 channels 支持同步通信,不浪费内存资源,但会阻塞当前的协程,而缓存 channels 支持异步通信,预先指定缓存区大小,但可能浪费更多内存资源。

在实际使用中,我们需要根据具体业务场景选择合适的通信方式,从而提升整个应用程序的性能和稳定性。

后端开发标签