Golang 并发机制-3:通道(channels)机制详解

news2025/2/5 13:01:41

并发编程是一种创建性能优化且响应迅速的软件的强大方法。Golang(也称为 Go)通过通道(channels)这一特性,能够可靠且优雅地实现并发通信。本文将揭示通道的概念,解释其在并发编程中的作用,并提供有关如何通过无缓冲通道和有缓冲通道发送或接收数据的见解。

介绍通道

在Go语言中,通道是一个基本的特性,它支持在gooutine(并发执行线程)之间进行安全和同步的通信。它们充当管道,通过它可以在程序之间传递数据,促进并发程序中的协调和同步。

通道是单向的,这意味着它们既可以用于发送数据(<- chan)也可以用于接收数据(chan <- )。这种单向性有助于在程序间实现清晰和可控的数据流。
在这里插入图片描述

发送和接收数据

1. 非缓存通道

无缓冲通道是一种同时发送和接收数据的通道。当在无缓冲通道上发送值时,发送方将阻塞,直到有相应的接收方准备接收该数据。同样,接收器将阻塞,直到有可用的数据接收。

下面是一个说明使用非缓冲通道的示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int) // Create an unbuffered channel

    go func() {
        ch <- 42 // Send data into the channel
    }()

    // time.Sleep(time.Second) // Give the Goroutine time to execute

    value := <-ch // Receive data from the channel
    fmt.Println("Received:", value)
}

在这个例子中,一个线程将值 42发送到未缓冲的通道 ch.主线程接收它,程序将阻塞,直到发送方和接收方都准备好。

2. 缓存通道

缓冲通道支持使用指定的缓冲区大小异步发送和接收数据。这意味着只要缓冲区未满,就可以在不等待接收器的情况下向通道发送多个值。同样,只要缓冲区不是空的,接收方可以从通道中读取数据,而不需要等待发送方。

下面是一个使用缓冲通道的例子:

package main

import "fmt"

func main() {
    ch := make(chan string, 2) // Create a buffered channel with a capacity of 2

    ch <- "Hello" // Send data into the channel
    ch <- "World"

    fmt.Println(<-ch) // Receive data from the channel
    fmt.Println(<-ch)
}

在本例中,我们创建了容量为2的缓冲通道ch。我们可以在不阻塞的情况下向通道发送两个值,然后接收并打印这些值。当你希望解耦发送方和接收方时,缓冲通道非常有用,允许它们在缓冲区大小约束下独立工作。

  • 同步通道

Go中的通道同步是一种技术,用于通过使用通道来协调和同步Goroutines(并发线程)的执行。通道促进了程序之间安全和有序的通信,允许它们在完成特定任务或准备好数据时相互发送信号。这种同步机制对于确保运行例程以受控和同步的方式执行至关重要。

下面是通道同步有用的一些常见场景:

  1. 等待Goroutines完成:你可以使用通道来等待一个或多个Goroutines在继续主程序之前完成它们的任务。
  2. 协调并行任务:通道可用于协调并发执行任务的多个goroutine,确保它们以特定顺序完成工作或在特定点同步。
  3. 收集结果:通道可用于收集和聚合来自多个goroutine的结果,然后在所有goroutine完成其工作后处理它们。

让我们用例子来探索这些场景:

  • 等待goroute完成
package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d is working\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait() // Wait for all workers to finish
    fmt.Println("All workers have finished.")
}

在这个例子中,我们有三个工作程序。我们使用“同步”。WaitGroup’,等待所有工人完成他们的工作,然后打印“所有工人都完成了”。

  • 协同并行任务
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    ch := make(chan int)

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fmt.Printf("Goroutine %d is working\n", id)
            ch <- id // Send a signal to the channel when done
        }(i)
    }

    // Wait for all Goroutines to signal completion
    go func() {
        wg.Wait()
        close(ch) // Close the channel when all Goroutines are done
    }()

    for id := range ch {
        fmt.Printf("Received signal from Goroutine %d\n", id)
    }

    fmt.Println("All Goroutines have finished.")
}

在本例中,我们有三个执行工作并使用通道发出完成信号的goroutine。我们使用“同步”。WaitGroup '等待所有的Goroutine完成,并且一个单独的Goroutine侦听通道以知道每个Goroutine何时完成其工作。

  • 收集结果
package main

import (
    "fmt"
    "sync"
)

func worker(id int, resultChan chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    result := id * 2
    resultChan <- result // Send the result to the channel
}

func main() {
    var wg sync.WaitGroup
    resultChan := make(chan int, 3)

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, resultChan, &wg)
    }

    wg.Wait() // Wait for all workers to finish
    close(resultChan) // Close the channel when all results are sent

    for result := range resultChan {
        fmt.Printf("Received result: %d\n", result)
    }
}

在本例中,三个工作线程程序计算结果并将其发送到通道。主程序等待所有工作线程完成,关闭通道,然后从通道读取和处理结果。

这些示例说明了如何使用通道同步来协调和同步Go中各种并发编程场景中的Go例程。通道为线程间安全有序的通信提供了强大的机制,使编写行为可预测且可靠的并发程序变得更加容易。

Select 语句: 多路复用通道(Multiplexing Channels)

管理并发任务的关键工具之一是“select”语句。在本文中,我们将探讨“select”语句在多路复用通道中的作用,这是一种使Go程序员能够有效地同步和协调Go例程的技术。

当你有多个通过各种渠道进行通信的goroutine时,可能需要有效地协调它们的活动。“select”语句支持通过选择首个可以处理的通道操作来实现这一点。

下面是一个简单的例子,演示了在多路信道中使用“select”:

package main

import (
    "fmt"
    "time"
)

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

    go func() {
        time.Sleep(time.Second)
        ch1 <- "Message from Channel 1"
    }()

    go func() {
        time.Sleep(time.Millisecond * 500)
        ch2 <- "Message from Channel 2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }

    fmt.Println("Main function exits")
}

在本例中,我们有两个在两个不同通道上发送消息的goroutine, ch1 ch2 select语句选择可用的首个通道操作,允许我们从 ch1 ch2接收和打印消息。然后,程序继续执行main函数,演示使用“select”的通道复用功能。

  • select 带缺省分支

‘ select ’语句还支持‘ default ’情况,当您想要处理没有通道操作准备好的情况时,这很有用。这里有一个例子:

package main

import (
    "fmt"
    "time"
)

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

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

    select {
    case msg := <-ch:
        fmt.Println(msg)
    default:
        fmt.Println("No message received")
    }

    fmt.Println("Main function exits")
}

在本例中,我们有一个在通道‘ ch ’上发送消息的程序。然而,“select”语句包含一个“default”情况,用于处理在预期时间内没有消息到达的情况。这允许在没有任何通道操作就绪的情况下进行优雅的处理。

Go中的最佳实践和模式:扇出、扇入和关闭通道

当谈到编写干净高效的Go代码时,有一些最佳实践和模式可以显著提高并发程序的质量和性能。在本文中,我们将探讨两个基本实践:Fan-out, Fan-in(扇出、扇入)和关闭通道。这些模式是在Go应用程序中管理并发性和通信的强大工具。

在这里插入图片描述
在这里插入图片描述
  1. 扇出,扇入

Fan-out, Fan-in模式是一种并发设计模式,允许你跨多个goroutine分发工作,然后收集和合并结果。当处理可以并发处理然后进行聚合的任务时,此模式特别有用。

  • 扇出,扇入示例
package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

func worker(id int, input <-chan int, output chan<- int) {
	for number := range input {
		// Simulate some work
		time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
		output <- number * 2
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())

	input := make(chan int)
	output := make(chan int)

	const numWorkers = 3
	var wg sync.WaitGroup

	// Fan-out: Launch multiple workers
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			worker(id, input, output)
		}(i)
	}

	// Fan-in: Collect results
	go func() {
		wg.Wait()
		close(output)
	}()

	// Send data to workers
	go func() {
		for i := 1; i <= 10; i++ {
			input <- i
		}
		close(input)
	}()

	// Receive and process results
	for result := range output {
		fmt.Println("Result:", result)
	}
}

在本例中,我们创建了三个worker goroutine,它们执行一些模拟工作,然后将结果发送到输出通道。主程序生成输入数据,单独的程序使用扇入模式收集和处理结果。

  1. 关闭通道

关闭通道是发送数据传输完成信号和防止程序无限阻塞的基本做法。当你不再打算通过通道发送数据时,关闭通道以避免死锁是至关重要的。

package main

import "fmt"

func main() {
	dataChannel := make(chan int, 3)

	go func() {
		defer close(dataChannel) // Close the channel when done
		for i := 1; i <= 3; i++ {
			dataChannel <- i
		}
	}()

	// Receive data from the channel
	for num := range dataChannel {
		fmt.Println("Received:", num)
	}
}

在本例中,我们创建了一个容量为3的缓冲通道‘ dataChannel ’。在向通道发送三个值之后,我们使用‘ close ’函数关闭它。关闭任何接收器的通道信号,不再发送数据。这允许接收程序在处理完所有数据后优雅地退出。

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

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

相关文章

可视化大屏在石油方面的应用。

可视化大屏通过整合石油工业全链条数据&#xff0c;构建数字孪生驱动的运营监控体系&#xff0c;显著提升油气勘探、开采、储运及炼化的管理效能。其技术架构依托工业物联网&#xff08;IIoT&#xff09;实时采集钻井参数、管道压力、储罐液位等数据&#xff0c;通过OPC UA协议…

【学术投稿-2025年计算机视觉研究进展与应用国际学术会议 (ACVRA 2025)】从计算机基础到HTML开发:Web开发的第一步

会议官网&#xff1a;www.acvra.org 简介 2025年计算机视觉研究进展与应用&#xff08;ACVRA 2025&#xff09;将于2025年2月28-3月2日在中国广州召开&#xff0c;将汇聚世界各地的顶尖学者、研究人员和行业专家&#xff0c;聚焦计算机视觉领域的最新研究动态与应用成就。本次…

Axure PR 9 旋转效果 设计交互

大家好&#xff0c;我是大明同学。 这期内容&#xff0c;我们将学习Axure中的旋转效果设计与交互技巧。 旋转 创建旋转效果所需的元件 1.打开一个新的 RP 文件并在画布上打开 Page 1。 2.在元件库中拖出一个按钮元件。 创建交互 创建按钮交互状态 1.选中按钮元件&#xf…

Docker 部署教程jenkins

Docker 部署 jenkins 教程 Jenkins 官方网站 Jenkins 是一个开源的自动化服务器&#xff0c;主要用于持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;过程。它帮助开发人员自动化构建、测试和部署应用程序&#xff0c;显著提高软件开发的效率和质量…

计算图 Compute Graph 和自动求导 Autograd | PyTorch 深度学习实战

前一篇文章&#xff0c;Tensor 基本操作5 device 管理&#xff0c;使用 GPU 设备 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started PyTorch 计算图和 Autograd 微积分之于机器学习Computational Graphs 计算图Autograd…

接入DeepSeek大模型

接入DeepSeek 下载并安装Ollamachatbox 软件配置大模型 下载并安装Ollama 下载并安装Ollama&#xff0c; 使用参数ollama -v查看是否安装成功。 输入命令ollama list&#xff0c; 可以看到已经存在4个目录了。 输入命令ollama pull deepseek-r1:1.5b&#xff0c; 下载deepse…

【论文复现】粘菌算法在最优经济排放调度中的发展与应用

目录 1.摘要2.黏菌算法SMA原理3.改进策略4.结果展示5.参考文献6.代码获取 1.摘要 本文提出了一种改进粘菌算法&#xff08;ISMA&#xff09;&#xff0c;并将其应用于考虑阀点效应的单目标和双目标经济与排放调度&#xff08;EED&#xff09;问题。为提升传统粘菌算法&#xf…

UE Bridge混合材质工具

打开虚幻内置Bridge 随便点个材质点右下角图标 就能打开材质混合工具 可以用来做顶点绘制

基于 yolov8_pyqt5 自适应界面设计的火灾检测系统 demo:毕业设计参考

基于 yolov8_pyqt5 自适应界面设计的火灾检测系统 demo&#xff1a;毕业设计参考 【毕业设计参考】基于yolov8-pyqt5自适应界面设计的火灾检测系统demo.zip资源-CSDN文库 【毕业设计参考】基于yolov8-pyqt5自适应界面设计的火灾检测系统demo.zip资源-CSDN文库 一、项目背景 …

Linux 传输层协议 UDP 和 TCP

UDP 协议 UDP 协议端格式 16 位 UDP 长度, 表示整个数据报(UDP 首部UDP 数据)的最大长度如果校验和出错, 就会直接丢弃 UDP 的特点 UDP 传输的过程类似于寄信 . 无连接: 知道对端的 IP 和端口号就直接进行传输, 不需要建立连接不可靠: 没有确认机制, 没有重传机制; 如果因…

chrome浏览器chromedriver下载

chromedriver 下载地址 https://googlechromelabs.github.io/chrome-for-testing/ 上面的链接有和当前发布的chrome浏览器版本相近的chromedriver 实际使用感受 chrome浏览器会自动更新&#xff0c;可以去下载最新的chromedriver使用&#xff0c;自动化中使用新的chromedr…

第一个Qt开发实例(一个Push Button按钮和两个Label)【包括如何在QtCreator中创建新工程、代码详解、编译、环境变量配置、测试程序运行等】

目录 Qt开发环境QtCreator的安装、配置在QtCreator中创建新工程在Forms→mainwindow.ui中拖曳出我们要的图形按钮查看拖曳出按钮后的代码为pushButton这个图形添加回调函数编译工程关闭开发板上QT的GUI(选做)禁止LCD黑屏(选做)设置Qt运行的环境变量运行Qt程序如何让程序在系统启…

【react+redux】 react使用redux相关内容

首先说一下&#xff0c;文章中所提及的内容都是我自己的个人理解&#xff0c;是我理逻辑的时候&#xff0c;自我说服的方式&#xff0c;如果有问题有补充欢迎在评论区指出。 一、场景描述 为什么在react里面要使用redux&#xff0c;我的理解是因为想要使组件之间的通信更便捷…

【435. 无重叠区间 中等】

题目&#xff1a; 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 注意 只在一点上接触的区间是 不重叠的。例如 [1, 2] 和 [2, 3] 是不重叠的。 示例 1: 输入: intervals …

文献学习笔记:中风醒脑液(FYTF-919)临床试验解读:有效还是无效?

【中风醒脑液&#xff08;FYTF-919&#xff09;临床试验解读&#xff1a;有效还是无效&#xff1f;】 在发表于 The Lancet &#xff08;2024 年 11 月 30 日&#xff0c;第 404 卷&#xff09;的临床研究《Traditional Chinese medicine FYTF-919 (Zhongfeng Xingnao oral pr…

vue2语法速通

首先&#xff0c;git clone下来的项目要npm install下载依赖&#xff0c;如果是vue项目&#xff0c;运行通常npm run serve或者npm run dev vue速通一下 使用vite创建项目&#xff08;较快&#xff09; npm create vite 配置文件 src/ ├── assets/ # 存放…

【商品库存管理——差分、前缀和】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 3e510; int l[N], r[N], b[N]; int s1[N], s0[N]; int main() {int n, m;cin >> n >> m;for(int i 1; i < m; i){cin >> l[i] >> r[i];b[l[i]], b[r[i]1]--;}int a 0…

Linux基本指令2

07.man指令&#xff08;重要&#xff09;&#xff1a; Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是 man 语法: man [选项] 命令 man ls查看ls指令更多的说明。 man man&#xff1a; man指令就…

Android学习19 -- 手搓App

1 前言 之前工作中&#xff0c;很多时候要搞一个简单的app去验证底层功能&#xff0c;Android studio又过于重型&#xff0c;之前用gradle&#xff0c;被版本匹配和下载外网包折腾的堪称噩梦。所以搞app都只有找应用的同事帮忙。一直想知道一些简单的app怎么能手搓一下&#x…

人工智能导论-第3章-知识点与学习笔记

参考教材3.2节的内容&#xff0c;介绍什么是自然演绎推理&#xff1b;解释“肯定后件”与“否定前件”两类错误的演绎推理是什么意义&#xff0c;给出具体例子加以阐述。参考教材3.3节的内容&#xff0c;介绍什么是文字&#xff08;literal&#xff09;&#xff1b;介绍什么是子…