使用通道和模式

在这里插入图片描述

通过通道、选择语句和最佳实践掌握 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.mfbz.cn/a/264414.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构 -- 堆

一.堆的概念 1.1 堆是什么 堆也叫做优先队列&#xff0c;一些按照重要性或优先级来组织的对象称为优先队列。 1.2 为什么需要堆 在现实生活中&#xff0c;存在许多需要从一群人、一些任务或一些对象中找出“下一位最重要”目标的情况。例如&#xff1a;在平时处理事情的时候我…

事件和事件源

事件监听 在JS当中写事件监听是这个函数&#xff0c;写了这个函数&#xff0c;前面是DOM对象&#xff0c;当由DOM树和CSSOM树形成的渲染树也有这个监听&#xff0c;这个函数可以添加到DOM树&#xff0c;最后渲染树也有。渲染树会渲染标签当标签发生该事件就会执行这个函数。这个…

操作系统——进程管理算法和例题

1、概述 1.1 进程调度 当进程的数量往往多于处理机的个数&#xff0c;出现进程争用处理机的现象&#xff0c;处理机调度是对处理机进行分配&#xff0c;就是从就绪队列中&#xff0c;按照一定的算法&#xff08;公平、髙效&#xff09;选择一个进程并将处理机分配给它运行&am…

Python---搭建Python自带静态Web服务器

1. 静态Web服务器是什么&#xff1f; 可以为发出请求的浏览器提供静态文档的程序。 平时我们浏览百度新闻数据的时候&#xff0c;每天的新闻数据都会发生变化&#xff0c;那访问的这个页面就是动态的&#xff0c;而我们开发的是静态的&#xff0c;页面的数据不会发生变化。 …

帆软报表 - 数据显示为列表,但是数据仍全部显示在同一行上?

文章目录 1 问题截图2 解决办法3 原因分析3.1 数据设置&#xff1a;全是列表 1 问题截图 想要的效果&#xff1a;每行显示一组数据得到的效果&#xff1a;数据全部显示在一行&#xff0c;以逗号隔开 2 解决办法 修改扩展方向。将 “不扩展” 修改为 “纵向” 3 原因分析 3.1…

消除蛋蛋派

欢迎来到程序小院 消除蛋蛋派 玩法&#xff1a;消除游戏&#xff0c;三个相同形状的蛋蛋连成一条直线即可消除&#xff0c;点击鼠标左键移动球球进行消除&#xff0c; 可以使用道具&#xff0c;共有50关卡&#xff0c;快去闯关吧^^。开始游戏https://www.ormcc.com/play/gameS…

3. BlazorSignalRApp 结合使用 ASP.NET Core SignalR 和 Blazor

参考&#xff1a;https://learn.microsoft.com/zh-cn/aspnet/core/blazor/tutorials/signalr-blazor?viewaspnetcore-8.0&tabsvisual-studio 1.创建新项目 BlazorSignalRApp 2.添加项目依赖项 依赖项&#xff1a;Microsoft.AspNetCore.SignalR.Client 方式1 管理解决方案…

c语言:文件操作(2),认识各种文件操作函数

fgets 作用 fgets是C语言标准库中用于从文件中读取字符串的函数。 fgets函数从指定的文件流stream中读取最多n-1个字符&#xff0c;或者直到遇到换行符&#xff08;包括换行符在内&#xff09;&#xff0c;并将其存储到以str指向的字符数组中。读取的字符串会以null字符\0结…

模拟电路基础知识经典 200问,收藏这些就够了!

大家总说模电知识总是学不会&#xff0c;IC修真院为大家整理了模电经典200问&#xff0c;看看你掌握了多少&#xff0c;文末可以获取全部哦。 文末可领全部文档 1、半导体材料制作电子器件与传统的真空电子器件相比有什么特点? 答&#xff1a;频率特性好、体积小、功耗小&…

信息收集 - 谷歌hack

搜索引擎 FOFA网络空间测绘:https://fofa.info/ FOFA(FOcus on Assets)是一个网络空间搜索引擎,可以帮助用户快速定位和收集特定目标的信息。 ZoomEye:https://www.zoomeye.org ZoomEye 是一个网络空间搜索引擎,可以用于发现和收集特定目标的网络设备、Web应用程序、开放…

OpenCV-Python(18):图像梯度

目录 背景介绍及应用 学习目标 原理 Sobel算子和Scharr算子 Laplacian 算子 代码示例 重要提醒 背景介绍及应用 图像的梯度是指图像中每个像素点的强度变化情况。计算图像的梯度可以帮助我们了解图像中物体的边界和纹理等信息。梯度在计算机视觉和图像处理领域有着广泛…

【Amazon 实验③】Amazon WAF功能增强之追踪 Amazon WAF RequestID,排查误杀原因

文章目录 1. 方案介绍2. 架构图3. 操作演示 本实验将介绍如何利用 Amazon LambdaEdge&#xff0c;在 Amazon CloudFront 自定义错误页面 上展示每个由 Amazon WAF 返回的“403 Forbidden”错误的 Request ID。通过这个唯一的 WAF Request ID&#xff0c;网站运维工程师能够快速…

Swift 周报 第四十一期

文章目录 前言新闻和社区2024 年 Swift Student Challenge 公布现推出超过 30 个新的开发者活动 提案正在审查的提案 Swift论坛话题讨论推荐博文关于我们 前言 本期是 Swift 编辑组整理周报的第四十一期&#xff0c;每个模块已初步成型。各位读者如果有好的提议&#xff0c;欢…

不受父容器大小约束的TextView

序言 为了实现以下效果&#xff0c;特意开发了一个自定义控件。主要是红色的点赞数和评论数。 问题分析 自定义控件 该控件主要是在于忽略的父容器的大小限制&#xff0c;这样可以展示出全部内容。注意父容器的属性中需要下列配置。 package com.trs.myrb.view.count;impor…

[JS设计模式]Flyweight Pattern

Flyweight pattern 享元模式是一种结构化的设计模式&#xff0c;主要用于产生大量类似对象而内存又有限的场景。享元模式能节省内存。 假设一个国际化特大城市SZ&#xff1b;它有5个区&#xff0c;分别为nanshan、futian、luohu、baoan、longgang&#xff1b;每个区都有多个图…

Java 基础学习(十三)集合框架、List集合

1 集合框架 1.1 Collection 1.1.1 集合框架概述 Java 集合框架是一组实现了常见数据结构&#xff08;如列表、树集和哈希表等&#xff09;的类和接口&#xff0c;用于存储一组数据。 开发者在使用Java的集合类时&#xff0c;不必考虑数据结构和算法的具体实现细节&#xff…

使用selenium webdriver和mitmproxy代理模拟用户点击抓包(抓华为应用商城app数据)

文章目录 安装PythonMacWindows 安装程序需要的依赖安装chorm驱动编写代码自动化程序开始抓包 问题处理 本文简单记录一下使用selenium webdriver和mitmproxy代理模拟用户点击抓包的过程。 用于模拟真实的用户访问网站&#xff0c;达到抓包的目的。 作者水平有限&#xff0c;可…

深度解析自动化测试流程(纯干货)

今天就通过这篇文章给大家深度解析一下自动化测试的流程。 自动化测试的流程和功能测试其实挺相似的&#xff0c;整个流程也是按照需求分析及测试计划阶段、测试设计阶段、测试执行和测试总结阶段&#xff0c;总结下来就是下面一张图&#xff0c;ppt中纯手绘&#xff0c;效果不…

天猫数据分析(软件工具)-2023年11月天猫保健品行业分析报告:市场需求扩容,年轻人是主流群体

近年来&#xff0c;随着健康经济、颜值经济的兴起&#xff0c;越来越多的年轻人加入养生大军&#xff0c;成为保健食品市场上的一股新力量&#xff0c;带动市场扩容。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年11月份&#xff0c;天猫平台上保健食品的销量为24…

怎么提取视频中的背景音乐?

当我们在刷视频的时候&#xff0c;有时候听到一个背景音乐很好听&#xff0c;但是又不知道歌名&#xff0c;比如英语歌&#xff0c;这个时候我们很难找到这首歌&#xff0c;相信有很多朋友会遇到这样的问题&#xff0c;不知道怎么弄&#xff0c;下面小编给大家推荐一些方法帮助…