第 16 章 - Go语言 通道(Channel)

news2024/11/20 3:44:46

在Go语言中,channel 是一个非常重要的概念,它主要用于协程之间的通信。通过 channel,你可以安全地传递数据从一个协程到另一个协程,而不需要担心并发控制的问题。下面我们将详细介绍 channel 的不同类型及其使用方法,并通过具体例子来加深理解。

Channel 的定义和使用

在 Go 中,创建一个 channel 非常简单,可以使用内置的 make 函数。基本语法如下:

ch := make(chan int)

这条语句创建了一个传输整型数据的 channel。如果没有指定缓冲区大小,那么这个 channel 就是一个无缓冲 channel。

无缓冲通道

无缓冲通道是最简单的形式,当一个值被发送到一个无缓冲通道时,发送操作会阻塞直到有接收者准备好接收这个值。同样,如果尝试从一个无缓冲通道接收值,接收操作也会阻塞,直到有发送者发送一个值。

示例:

package main

import "fmt"

func main() {
    ch := make(chan int) // 创建一个无缓冲通道

    go func() { // 启动一个 goroutine 发送数据
        ch <- 42 // 阻塞,直到有人接收
    }()

    fmt.Println(<-ch) // 阻塞,直到有人发送
}

在这个例子中,我们创建了一个无缓冲的 int 类型 channel,并启动了一个新的 goroutine 来发送数字 42 到这个 channel。主函数中的 <-ch 操作会阻塞,直到接收到值。

有缓冲通道

与无缓冲通道不同,有缓冲通道允许在没有接收者的情况下存储多个值。创建带缓冲区的 channel 时,需要指定缓冲区的大小。如果缓冲区未满,发送操作不会阻塞;只有当缓冲区已满时,发送操作才会阻塞,直到有接收者取出一个值。

示例:

package main

import "fmt"

func main() {
    ch := make(chan int, 2) // 创建一个带缓冲区的通道,缓冲区大小为2

    ch <- 1 // 不会阻塞
    ch <- 2 // 不会阻塞

    go func() {
        fmt.Println(<-ch) // 输出: 1
        fmt.Println(<-ch) // 输出: 2
    }()

    // 主goroutine继续执行,因为已经有两个值在缓冲区内
    fmt.Println("Main function continues")
}

在这个例子中,我们创建了一个缓冲区大小为2的 int 类型 channel。我们可以连续两次向该 channel 发送值而不被阻塞,因为缓冲区有足够的空间来存储这些值。

结合案例

假设我们需要实现一个简单的生产者-消费者模型,其中生产者将一系列数字发送到一个 channel,而消费者从同一个 channel 接收并处理这些数字。

生产者-消费者模型:

package main

import (
    "fmt"
    "time"
)

func producer(ch chan<- int) {
    for i := 1; i <= 5; i++ {
        ch <- i // 发送数字
        time.Sleep(time.Second) // 模拟延迟
    }
    close(ch) // 关闭通道,表示没有更多的值要发送
}

func consumer(ch <-chan int) {
    for num := range ch {
        fmt.Printf("Received number: %d\n", num)
        time.Sleep(2 * time.Second) // 模拟处理时间
    }
}

func main() {
    ch := make(chan int, 3) // 创建一个带缓冲区的通道

    go producer(ch) // 启动生产者
    go consumer(ch) // 启动消费者

    time.Sleep(10 * time.Second) // 让main goroutine等待足够长的时间以确保所有任务完成
}

在这个例子中,producer 函数作为一个生产者,不断地向 channel 发送数字;consumer 函数作为一个消费者,从 channel 接收数字并打印出来。我们使用了带缓冲区的 channel 来减少生产者和消费者之间的阻塞情况,从而提高程序的效率。

以上就是关于 Go 语言中 channel 的基本介绍和使用案例。希望这些信息能帮助你更好地理解和应用 channel

当然可以,接下来我们将进一步探讨 channel 的一些高级用法,包括多路复用、选择器 select、同步和关闭通道等。

多路复用

在 Go 语言中,可以使用 select 语句来监听多个 channel 的通信状态。这使得程序能够同时处理多个 channel 上的数据流,而不需要为每个 channel 单独编写循环或使用多个 goroutine。

示例:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(2 * time.Second)
		ch1 <- "from ch1"
	}()

	go func() {
		time.Sleep(1 * time.Second)
		ch2 <- "from ch2"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-ch1:
			fmt.Println(msg1)
		case msg2 := <-ch2:
			fmt.Println(msg2)
		}
	}
}

在这个例子中,我们创建了两个带有延迟发送消息的 channel,并通过 select 语句监听这两个 channelselect 会随机选择一个已经准备好的 case 执行,这样可以有效地处理并发情况下的多路复用。

同步

channel 可以用于同步 goroutine 的执行。例如,可以在一个 goroutine 完成其工作后通过 channel 发送一个信号给其他 goroutine。

示例:

package main

import (
	"fmt"
	"time"
)

func worker(done chan bool) {
	fmt.Println("Working...")
	time.Sleep(time.Second)
	fmt.Println("Done")

	done <- true // 工作完成后发送信号
}

func main() {
	done := make(chan bool, 1)
	go worker(done)

	<-done // 等待工作完成的信号
	fmt.Println("Worker finished.")
}

这里,worker 函数在完成工作后会通过 done channel 发送一个信号,主函数则通过 <-done 阻塞等待这个信号,从而实现了同步。

关闭通道

当不再向 channel 发送数据时,应该关闭该 channel。关闭 channel 后,不能再向其发送数据,但是仍然可以从已关闭的 channel 中接收数据,直到所有数据都被读取完毕。

示例:

package main

import "fmt"

func produce(ch chan<- int) {
	for i := 1; i <= 5; i++ {
		ch <- i
	}
	close(ch) // 关闭通道
}

func consume(ch <-chan int) {
	for v := range ch {
		fmt.Println(v)
	}
}

func main() {
	ch := make(chan int)
	go produce(ch)
	consume(ch)
}

在这个例子中,produce 函数在发送完所有数据后关闭了 ch channel。consume 函数使用 for v := range ch 循环来接收数据,直到 ch 被关闭并且所有数据都已被读取。

错误处理

在处理 channel 时,可能会遇到错误,如从已关闭的 channel 接收数据。Go 提供了一种优雅的方式来处理这种情况,即通过多值赋值来检查是否从 channel 接收到了值:

v, ok := <-ch
if !ok {
    // channel 已经关闭且没有更多数据可读
}

总结

通过上述示例,我们了解了如何使用 channel 进行多路复用、同步和关闭操作。channel 是 Go 语言中非常强大的特性,它不仅简化了并发编程,还提高了代码的可读性和可靠性。掌握 channel 的使用对于编写高效的 Go 应用至关重要。

接下来我们将进一步探讨 channel 的一些更高级的特性和应用场景,包括使用 context 控制 channel 的生命周期、超时处理、以及一些常见的设计模式。

使用 context 控制 channel 的生命周期

在 Go 语言中,context 包提供了一种方式来取消长时间运行的操作或 goroutine。通过将 contextchannel 结合使用,可以更灵活地管理并发任务的生命周期。

示例:

package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context, done chan<- bool) {
	for {
		select {
		case <-ctx.Done(): // 监听 context 的 Done 通道
			fmt.Println("Worker received cancel signal")
			done <- true
			return
		}
		fmt.Println("Working...")
		time.Sleep(1 * time.Second)
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // 确保在退出时取消 context

	done := make(chan bool)
	go worker(ctx, done)

	// 模拟主程序运行一段时间
	time.Sleep(5 * time.Second)

	fmt.Println("Cancelling context")
	cancel() // 取消 context

	<-done // 等待 worker 完成
	fmt.Println("Worker finished.")
}

在这个例子中,worker 函数通过 select 语句监听 contextDone 通道。当 context 被取消时,Done 通道会被关闭,worker 函数会收到信号并停止工作。主函数通过调用 cancel() 方法来取消 context,从而终止 worker 函数。

超时处理

在某些情况下,你可能需要在一定时间内完成某个操作,否则就放弃。Go 语言提供了 select 语句和 time.After 函数来实现超时处理。

示例:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan string)

	go func() {
		time.Sleep(3 * time.Second)
		ch <- "result"
	}()

	select {
	case res := <-ch:
		fmt.Println("Result received:", res)
	case <-time.After(2 * time.Second):
		fmt.Println("Timeout")
	}
}

在这个例子中,select 语句同时监听 ch 通道和 time.After 返回的通道。如果 ch 在 2 秒内没有接收到结果,time.After 通道会被触发,从而输出 “Timeout”。

常见的设计模式

扇出/扇入(Fan-out/Fan-in)

扇出/扇入是一种常见的并发模式,用于将任务分发给多个 goroutine 并收集结果。

示例:

package main

import (
	"fmt"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Println("Worker", id, "processing job", j)
		time.Sleep(time.Second) // 模拟处理时间
		results <- j * 2
	}
}

func main() {
	const numJobs = 5
	jobs := make(chan int, numJobs)
	results := make(chan int, numJobs)

	// 启动多个 worker
	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}

	// 分发任务
	for j := 1; j <= numJobs; j++ {
		jobs <- j
	}
	close(jobs)

	// 收集结果
	for a := 1; a <= numJobs; a++ {
		<-results
		fmt.Println("Collected result")
	}
}

在这个例子中,我们创建了多个 worker goroutine 来处理任务。任务通过 jobs 通道分发,结果通过 results 通道收集。main 函数负责分发任务并收集所有结果。

总结

通过上述示例,我们深入了解了 channel 的一些高级用法,包括使用 context 控制 channel 的生命周期、超时处理以及常见的设计模式。这些技术不仅增强了并发编程的能力,还提高了代码的健壮性和可维护性。掌握这些高级特性,可以帮助你在实际项目中更高效地使用 channel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

构建安全的数据库环境:群晖NAS安装MySQL和phpMyAdmin详细步骤

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 本文将详细讲解如何在群晖NAS上安装MySQL及其数据库管理…

【c++丨STL】list的使用

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 list简介 一、list的默认成员函数 构造函数(constructor) 析构函数 赋值重载 二、list的迭代器接口 迭代器的功能分类 三、list的容量…

如何编译 Cesium 源码

如何编译 Cesium 源码 Cesium 是一个开源的 JavaScript 库&#xff0c;用于构建 3D 地球和地图应用程序。它提供了一套强大的 API 和工具&#xff0c;使开发者能够创建丰富的地理空间应用。本文将指导您如何从 GitHub 下载 Cesium 源码&#xff0c;并在本地进行编译。 TilesB…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

全志T113双核异构处理器的使用基于Tina Linux5.0——RTOS编译开发说明

3、RTOS编译开发说明 3.1、RTOS SDK与TinaLinux开发环境 RTOS SDK相关代码已集成到Tina Linux开发环境&#xff0c;Tina Linux开发环境下的rtos子目录即为RTOS开发环境。 ├──brandy ├──bsp ├──build ├──buildroot ├──build.sh >build/top_build.sh ├──…

汽车资讯新篇章:Spring Boot技术启航

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04目录1. Alopex: A Computational Framework for Enabling On-Device Function Calls with LLMs摘要&#xff1a;研究背景&…

细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的方法

目录 一、DMA基础知识 1、DMA简介 (1)DMA控制器 (2)DMA流 (3)DMA请求 (4)仲裁器 (5)DMA传输属性 2、源地址和目标地址 3、DMA传输模式 4、传输数据量的大小 5、数据宽度 6、地址指针递增 7、DMA工作模式 8、DMA流的优先级别 9、FIFO或直接模式 10、单次传输或突…

review-消息中间件MQ

RabbitMQ RabbitMQ&#xff0c;作为当今流行的开源消息代理软件&#xff0c;以其卓越的可靠性、灵活性和易用性在微服务架构和分布式系统中扮演着至关重要的角色。它不仅能够确保消息在不同系统组件间的高效传递&#xff0c;还能通过其高级消息队列协议&#xff08;AMQP&#x…

使用 .NET 创建新的 WPF 应用

本教程介绍如何使用 Visual Studio 创建新的 Windows Presentation Foundation &#xff08;WPF&#xff09; 应用。 使用 Visual Studio&#xff0c;可以向窗口添加控件以设计应用的 UI&#xff0c;并处理这些控件中的输入事件以与用户交互。 在本教程结束时&#xff0c;你有一…

【青牛科技】视频监控器应用

1、简介&#xff1a; 我司安防产品广泛应用在视频监控器上&#xff0c;产品具有性能优良&#xff0c;可 靠性高等特点。 2、图示&#xff1a; 实物图如下&#xff1a; 3、具体应用&#xff1a; 标题&#xff1a;视频监控器应用 简介&#xff1a;视频监控器工作原理是光&#x…

Android 项目依赖库无法找到的解决方案

目录 错误信息解析 解决方案 1. 检查依赖版本 2. 检查 Maven 仓库配置 3. 强制刷新 Gradle 缓存 4. 检查网络连接 5. 手动下载依赖 总结 相关推荐 最近&#xff0c;我在编译一个 Android 老项目时遇到了一个问题&#xff0c;错误信息显示无法找到 com.gyf.immersionba…

esp32c3开发板通过micropython的mqtt库连MQTT物联网消息服务器

MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息协议&#xff0c;旨在设备之间进行通信&#xff0c;尤其是在网络条件较差的情况下。MQTT v3.1.1 和 MQTT v5 是该协议的两个主要版本。 MQTT v3.1.1&#xff1a; 优点&#xff…

2、 家庭网络发展现状

上一篇我们讲了了解家庭网络历史(https://blog.csdn.net/xld_hung/article/details/143639618?spm1001.2014.3001.5502),感兴趣的同学可以看对应的文章&#xff0c;本章我们主要讲家庭网络发展现状。 关于家庭网络发展现状&#xff0c;我们会从国内大户型和小户型的网络说起&…

element ui 走马灯一页展示多个数据实现

element ui 走马灯一页展示多个数据实现 element ui 走马灯一页展示多个数据实现 element ui 走马灯一页展示多个数据实现 主要是对走马灯的数据的操作&#xff0c;先看js处理 let list [{ i: 1, name: 1 },{ i: 2, name: 2 },{ i: 3, name: 3 },{ i: 4, name: 4 },]let newL…

linux文件与重定向

目录 一、共识原理 二、回顾C语言文件函数 1.fopen 2.fwrite 3.fclose 三、文件系统调用 1.open 2.write 3.访问文件的本质 4.stdin&&stdout&&stderror 5.文件的引用计数 四、重定向 1.文件描述符的分配规则 2. 输出重定向 3.重定向系统调用 4.…

CS DAC的Matlab建模与电路设计

在模拟电路设计的复杂世界里&#xff0c;每一个细节都至关重要。Current Steering DAC作为模拟数字转换的核心组件&#xff0c;其设计和性能优化一直是工程师们追求的目标。 “什么是Current Steering DAC&#xff1f; CS DAC通过控制电流源的开关&#xff0c;将数字输入信号…

网络传输:网卡、IP、网关、子网掩码、MAC、ARP、路由器、NAT、交换机

目录 网卡IP网络地址主机地址子网子网掩码网关默认网关 MACARPARP抓包分析 路由器NATNAPT 交换机 网卡 网卡(Network Interface Card&#xff0c;简称NIC)&#xff0c;也称网络适配器。 OSI模型&#xff1a; 1、网卡工作在OSI模型的最后两层&#xff0c;物理层和数据链路层。物…

STM32完全学习——系统时钟设置

一、时钟框图的解读 首先我们知道STM32在上电初始化之后使用的是内部的HSI未经过分频直接通过SW供给给系统时钟&#xff0c;由于内部HSI存在较大的误差&#xff0c;因此我们在系统完成上电初始化&#xff0c;之后需要将STM32的时钟切换到外部HSE作为系统时钟&#xff0c;那么我…

基于Java Springboot滁州市特产销售系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…