使用通道和模式

news2025/2/1 11:49:49

在这里插入图片描述

通过通道、选择语句和最佳实践掌握 Go 中的并发编程

并发编程是构建高效和响应迅速的软件的强大范例。Go,也被称为 Golang,通过通道提供了一种健壮且优雅的解决方案来进行并发通信。在这篇文章中,我们将探讨通道的概念、它们在并发编程中的作用,以及如何使用无缓冲和有缓冲的通道发送和接收数据。

通道简介

在 Go 中,通道是一种基本特性,它们使 Goroutines(并发执行的线程)之间能够进行安全和同步的通信。它们作为数据在 Goroutines 之间传递的通道,有助于并发程序的协调和同步。

通道是单向的,这意味着它们可以用于发送数据(<- chan)或接收数据(chan <-)。这种单向性有助于在 Goroutines 之间确立明确和受控的数据流。

发送和接收数据

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)
}

在这个示例中,一个 Goroutine 向无缓冲通道 ch 发送值 42,然后主 Goroutine 进行接收。程序将会阻塞,直到发送者和接收者都准备好。

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(并发线程)执行的技术。通道促进了 Goroutines 之间的安全和有序的通信,使它们能够在特定任务完成或数据准备好时相互发出信号。这种同步机制对于确保 Goroutines 以受控和同步的方式执行至关重要。

以下是一些常见的场景,其中通道同步非常有用:

  1. 等待 Goroutines 完成:你可以使用通道来等待一个或多个 Goroutines 完成它们的任务,然后再继续主程序的执行。
  2. 协调并行任务:通道可以被用来编排多个 Goroutines 同时执行任务,确保它们按照特定的顺序完成工作或在特定点同步。
  3. 收集结果:通道可以用来收集和聚合来自多个 Goroutines 的结果,然后在所有 Goroutines 完成它们的工作后对这些结果进行处理。

让我们通过示例来探索这些场景:

1. 等待 Goroutines 完成

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.")
}

在这个示例中,我们有三个工作 Goroutines。我们使用 sync.WaitGroup 来等待它们都完成工作后再打印“所有工作者都已完成”。

2. 协调并行任务

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.")
}

在这个示例中,我们有三个 Goroutines 执行工作,并使用一个通道来发出它们完成的信号。我们使用 sync.WaitGroup 来等待所有 Goroutines 完成,而另一个独立的 Goroutine 则监听通道,以知道每个 Goroutine 何时完成其工作。

3. 收集结果

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)
    }
}

在这个示例中,三个工作 Goroutines 计算结果并将它们发送到一个通道。主 Goroutine 等待所有工作者完成,关闭通道,然后从通道中读取和处理结果。

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

选择语句:多路复用通道

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

使用 select 进行通道的多路复用

当您有多个 Goroutines 通过各种通道进行通信时,您可能需要有效地协调它们的活动。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")
}

在这个示例中,我们有两个 Goroutines 在两个不同的通道 ch1ch2 上发送消息。select 语句选择第一个变得可用的通道操作,允许我们从 ch1ch2 接收并打印消息。然后程序继续执行主函数,展示了使用 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")
}

在这种情况下,我们有一个 Goroutine 在通道 ch 上发送消息。然而,select 语句包括一个 default 情况,用于处理在预期时间内没有消息到达的情况。这允许对没有任何通道操作准备好的情况进行优雅的处理。

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

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

1. 扇出、扇入

扇出、扇入 模式是一个并发设计模式,它允许您在多个 Goroutines 之间分发工作,然后收集和整合结果。当处理可以并发处理然后聚合的任务时,这种模式尤其有用。

扇出、扇入的示例
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)
	}
}

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

2. 关闭通道

关闭通道是一个重要的实践,用于标记数据传输的完成并防止 Goroutines 无限期地阻塞。当您不再计划通过它们发送数据时,关闭通道是至关重要的,以避免死锁。

关闭通道的示例
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函数关闭它。关闭通道向任何接收者发出信号,表示不会再发送更多的数据。这使得接收的 Goroutine 在所有数据都已被处理完毕后可以优雅地退出。

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

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

相关文章

Qt配置opencv,cmake编译参考笔记,已成功

Qt版本&#xff1a;Qt5.14.2 opencv&#xff1a;4.5.4&#xff08;不要用4.5.5&#xff01;&#xff01;很坑别问我为什么知道&#xff09; cmake&#xff1a;下的最新版本 前言&#xff1a;为什么非得要用cmake编译呢&#xff1f;跳过cmake不好吗&#xff1f; 之前用的opencv…

pickle反序列化

文章目录 基础知识pickle简介可序列化对象object.__reduce__() 函数 pickle过程详细解读opcode简介pickletools 漏洞利用利用思路如何手写opcode 工具pker实战例题[MTCTF 2022]easypickle[HZNUCTF 2023 preliminary]pickle 基础知识 pickle简介 与PHP类似&#xff0c;python也…

内存管理学习

内存管理 在计算系统中&#xff0c;通常存储空间分为两种&#xff1a;内部存储空间和外部存储空间。 内部存储空间通常访问速度比较快&#xff0c;能够按照变量地址随机访问&#xff0c;也就是我们通常所说的RAM&#xff08;随机存储器&#xff09;&#xff0c;可以把它理解为…

两种方法解决win10开机慢,经验分享

方法一&#xff1a; 1、按快捷键“winR”打开 运行窗口。 2、这时候输入“msconfig”后 &#xff0c;点击“确定”或者按“ENTER”键。 3、这时候会打开一个名为“系统配置”的窗口&#xff0c; 在“常规”选项框下 勾选“有选择的启动”下的“加载系统服务”和“加载启动项”。…

Python的环境搭建环境配置()

Python 环境搭建 一,下载Python 1.去官网 www.python.org 下载环境 2.如图点击Download 3.选择Windows 4.如图直接下载 5.直接勾选 6.后面就一直默认选项 Win11 安装目录 不能放在C盘的ProgramFIle路径下 二,测试环境是否安装成功 1.winR 输入cmd 2.输入python --versio…

[已解决] Ubuntu远程桌面闪退+登录显示“远程桌面由于数据加密错误 , 这个会话将结束“

两个月前&#xff0c;由于跑代码在Ubuntu配置环境&#xff0c;乱七八糟的下载了很多东西&#xff0c;导致了一系列问题..... 问题1 Ubuntu远程桌面闪退 实验室有两台服务器&#xff0c;IP后三位分别为141和142&#xff0c;其中141在输入密码后立即闪退&#xff0c;142可以正常…

110基于matlab的混合方法组合的极限学习机和稀疏表示进行分类

基于matlab的混合方法组合的极限学习机和稀疏表示进行分类。通过将极限学习机&#xff08;ELM&#xff09;和稀疏表示&#xff08;SRC&#xff09;结合到统一框架中&#xff0c;混合分类器具有快速测试&#xff08;ELM的优点&#xff09;的优点&#xff0c;且显示出显着的分类精…

华为鸿蒙开发适合哪些人学习?

随着鸿蒙系统的崛起&#xff0c;越来越多的人开始关注鸿蒙开发&#xff0c;并希望成为鸿蒙开发者。然而&#xff0c;鸿蒙开发并不适合所有人&#xff0c;那么哪些人最适合学习鸿蒙开发呢&#xff1f;本文将为您总结鸿蒙开发适合的人群。 一、具备编程基础的人 学习鸿蒙开发需要…

python报错A value is trying to be set on a copy of a slice

加入.copy()即可避免该报错提示 原代码&#xff1a; df5df4.drop_duplicates() print(df5.shape)df5[班型中文名称]df5[班型名称]-A print(df5.head()) 输出结果&#xff1a; 修改后代码&#xff1a; df5df4.drop_duplicates().copy() print(df5.shape)df5[班型中文名称]df…

MyBatis关联查询(三、多对多查询)

MyBatis关联查询&#xff08;三、多对多查询&#xff09; 需求&#xff1a;查询角色及角色赋予的用户信息。 分析&#xff1a;一个用户可以拥有多个角色&#xff0c;一个角色也可以赋予多个用户&#xff0c;用户和角色为双向的一对多关系&#xff0c;多对多关系其实我们看成是…

2. 行为模式 - 命令模式

亦称&#xff1a; 动作、事务、Action、Transaction、Command 意图 命令模式是一种行为设计模式&#xff0c; 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中&#xff0c; 且能实现可撤销…

SVM —— 代码实现

SMO 算法的实现步骤&#xff1a; 代码如下&#xff1a; import numpy as np import matplotlib.pyplot as plt import seaborn as sns import random# 设置中文字体为宋体&#xff0c;英文字体为 times new roman sns.set(font"SimSun", style"ticks", fo…

从零开始创建GPTs 人人都可以编写自己的ChatGPT产品

在这个人工智能迅猛发展的时代&#xff0c;GPT&#xff08;生成式预训练变换器&#xff09;已经成为一项令人兴奋的技术&#xff0c;它打开了创意和知识的新大门。无论你是一名编程新手、一位热爱探索的学生&#xff0c;还是对未来充满好奇的专业人士&#xff0c;GPTs都可以为你…

Win7如何修改MAC地址

MAC地址&#xff0c;又叫做物理地址、硬件地址&#xff0c;是用来定义网络设备的位置&#xff0c;一般情况下&#xff0c;MAC地址在网卡中是固定的&#xff0c;但不排除有人手动去修改自己的MAC地址。win7如何修改MAC地址?其实修改MAC地址的方法很简单&#xff0c;可以通过硬件…

【经典LeetCode算法题目专栏分类】【第8期】滑动窗口:最小覆盖子串、字符串排列、找所有字母异位词、 最长无重复子串

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

[AutoSar]状态管理(五)Dcm与BswM、EcuM的复位实现

目录 关键词平台说明前言一、总体流程二、配置2.1 DCM and DEM2.2 BSWM2.2.1 Mode Notifaication Port2.2.2 Rules 2.3 service port2.3.1 做好DCM-->BSWM 和DCM -->SWC_Diag 的server port mapping2.3.2 做好BSWM ESH_ModeNotification 的server port mapping 2.4 SWC 中…

美好蕴育润康:为孕产期女性量身定制的专业营养

如今&#xff0c;孕产期是女性人生中特别而又重要的阶段。这段时间&#xff0c;孕期妈妈经常饱受许多痛苦和不适&#xff0c;更需要额外的关爱和呵护&#xff0c;以确保母婴健康。为了满足孕产期女性特殊的营养需求&#xff0c;美好蕴育润康应运而生&#xff0c;成为她们身边的…

web网页端使用webSocket实现语音通话功能(SpringBoot+VUE)

写在前面 最近在写一个web项目&#xff0c;需要实现web客户端之间的语音通话&#xff0c;期望能够借助webSocket全双工通信的方式来实现&#xff0c;但是网上没有发现可以正确使用的代码。网上能找到的一个代码使用之后只能听到“嘀嘀嘀”的杂音 解决方案&#xff1a;使用Jso…

QC/PD快充电源产品应用介绍

• 原边650-700V SJ MOSFET采用低FOM值的ESM 技术&#xff0c;有利于提高系统效 率&#xff0c; 以及更佳的EAS和EMI等特性&#xff0c;对于一些不含PFC电路的系统更友好。 • 副边采用低FOM值的SGT同步整流电路&#xff0c;相比肖特基二极管整流能有更低的 损耗&#xff0c;有…

2023年12月GESP认证C++等级考试(三级)真题试卷

2023年12月GESP认证C等级考试&#xff08;三级&#xff09;真题试卷 题目总数&#xff1a;27 总分数&#xff1a;100 选择题 第 1 题 单选题 下⾯C数组的定义中&#xff0c;会丢失数据的是( )。 A. char dict_key[] {p,t,o}; B. int dict_value[] {33,22,11}; C. char …