Go语言中的并发模式

news/2024/11/7 10:15:52/文章来源:https://www.cnblogs.com/cheyunhua/p/18523784

Go语言中的并发模式

原创 Go 源自开发者
 
源自开发者
专注于提供关于Go语言的实用教程、案例分析、最新趋势,以及云原生技术的深度解析和实践经验分享。
373篇原创内容

Go语言以其并发性和轻量级的goroutine而闻名,学习如何使用和处理它们是最具挑战性的任务。在本文中,我将展示一些并发模式及其使用场景,以帮助您识别所需场景的模式。

1. Goroutine

package main

import (
    "fmt"
    "time"
)

func main() {
    go sayHello() // 启动goroutine
    time.Sleep(1 * time.Second) // 等待goroutine完成
}

func sayHello() {
    fmt.Println("Hello, World!")
}

使用场景

当您需要执行非阻塞操作时,例如在Web服务器中处理请求或执行后台任务。

优点

  • 轻量级且易于启动。
  • 提供简单的并发性,无需复杂的结构。

缺点

  • 如果共享数据处理不当,可能导致竞争条件。
  • 由于执行流程非线性,调试可能更加复杂。

2. 通道(Channel)

package main

import (
    "fmt"
)

func main() {
    ch := make(chan string) // 创建通道

    go func() {
        ch <- "Hello from Goroutine!" // 发送数据到通道
    }()

    message := <-ch // 从通道接收数据
    fmt.Println(message)
}

使用场景

在goroutine之间通信或同步执行。

优点

  • 在goroutine之间安全通信。
  • 简单的同步机制。

缺点

  • 如果过度使用或误用,可能引入复杂性。
  • 如果通道处理不当,可能发生死锁。

3. 工作池(Worker Pool)

package main

import (
    "fmt"
    "sync"
)

type Job struct {
    ID int
}

func worker(id int, jobs <-chan Job, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job.ID)
    }
}

func main() {
    const numWorkers = 3
    jobs := make(chan Job, 10) // 缓冲通道
    var wg sync.WaitGroup

    for w := 1; w <= numWorkers; w++ {
        wg.Add(1)
        go worker(w, jobs, &wg)
    }

    for j := 1; j <= 5; j++ {
        jobs <- Job{ID: j} // 发送任务到通道
    }
    close(jobs) // 关闭任务通道
    wg.Wait() // 等待所有工作完成
}

使用场景

当您有许多任务可以并发处理,但希望限制并发操作的数量时。

优点

  • 通过控制并发goroutine的数量来限制资源使用。
  • 易于高效地处理大量任务。

缺点

  • 比简单的goroutine实现更复杂。
  • 需要仔细管理任务分配和完成。

4. 扇出扇入(Fan-Out, Fan-In)

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        results <- job * 2 // 处理任务
    }
}

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)
    var wg sync.WaitGroup

    for w := 1; w <= 3; w++ { // 3个工作者
        wg.Add(1)
        go worker(w, jobs, results, &wg)
    }

    go func() {
        wg.Wait()
        close(results) // 完成后关闭结果通道
    }()

    for j := 1; j <= 5; j++ {
        jobs <- j // 发送任务
    }
    close(jobs) // 关闭任务通道

    for result := range results {
        fmt.Println("Result:", result) // 收集结果
    }
}

使用场景

当您需要将工作分配给多个goroutine并整合结果时。

优点

  • 高效地平衡多个工作者之间的工作负载。
  • 简化结果收集。

缺点

  • 由于多个通道,稍微复杂一些。
  • 需要仔细管理同步。

5. 用于取消的上下文(Context for Cancellation)

package main

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

func doWork(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Work canceled")
            return
        default:
            fmt.Println("Working...")
            time.Sleep(500 * time.Millisecond) // 模拟工作
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    go doWork(ctx)

    time.Sleep(2 * time.Second) // 让它工作一段时间
    cancel() // 取消工作
    time.Sleep(1 * time.Second) // 等待取消
}

使用场景

当您需要管理goroutine的取消或超时时。

优点

  • 提供一种结构化的方式来取消goroutine。
  • 可以使用单个上下文管理多个goroutine。

缺点

  • 如果管理不当,可能会使逻辑复杂化。
  • 需要理解上下文传播。

6. 管道(Pipeline)

package main

import (
    "fmt"
)

func square(input <-chan int, output chan<- int) {
    for num := range input {
        output <- num * num // 平方数
    }
    close(output) // 完成后关闭输出
}

func main() {
    input := make(chan int)
    output := make(chan int)

    go square(input, output)

    for i := 1; i <= 5; i++ {
        input <- i // 发送数字
    }
    close(input) // 关闭输入通道

    for result := range output {
        fmt.Println("Squared:", result) // 打印结果
    }
}

使用场景

当您希望分阶段处理数据时。

优点

  • 促进模块化和关注点分离。
  • 更容易管理和理解数据流。

缺点

  • 随着阶段的增多,可能变得复杂。
  • 需要仔细管理通道。

7. 速率限制(Rate Limiting)

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(1 * time.Second) // 设置速率限制
    defer ticker.Stop()

    go func() {
        for range ticker.C {
            fmt.Println("Tick: Doing work...")
            // 在这里执行工作
        }
    }()

    // 模拟工作5秒
    time.Sleep(5 * time.Second)
}

使用场景

当您需要限制操作速率时,例如API调用。

优点

  • 控制操作频率的简单有效方法。
  • 减少资源争用。

缺点

  • 如果限制过于严格,可能会延迟处理。
  • 需要仔细配置速率限制。

8. Select语句

package main

import (
    "fmt"
    "time"
)

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

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

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

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("Timeout!")
    }
}

使用场景

当您需要等待多个通道并根据哪个通道首先接收到数据来执行操作时。

优点

  • 提供处理多个异步操作的方法。
  • 可以有效地实现超时。

缺点

  • 如果处理不当,可能导致复杂的逻辑。
  • 需要仔细处理所有情况以避免信号丢失。

总结

这些模式是编写Go语言并发程序的基础。每种模式都有其使用场景、优点和缺点,您可能会发现结合多种模式可以为您的应用程序提供最佳解决方案。仔细考虑应用程序的具体需求,并选择合适的模式。

图片


文章精选

使用 Go 语言连接并操作 SQLite 数据库

Go语言官方团队推荐的依赖注入工具

替代zap,Go语言官方实现的结构化日志包

Go语言常见错误 | 不使用function option模式

必看| Go语言项目结构最佳实践

 

源自下载
点击添加进交流群,免费领取Go语言学习资料!

 

Golang · 目录
上一篇在 Docker 中运行 Golang 应用程序
阅读原文
阅读 1278
 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高级程序语言设计第五次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2024C/homework/13298 学号:102400127 姓名:王子涵u9的第十问不会写

Android studio 代理设置和取消方式

1、 2、 C:\Users\XXXX 用户\.gradle 打开 gradle.properties

如何在零售行业应用AI

AI在零售行业的应用是一个重要的趋势,如何在零售业中成功应用人工智能技术,包括:1、步骤的明确、数据的分析、顾客体验的改善以及未来发展方向的展望。首先,明确零售业中应用AI的目标和步骤。这包括确定要解决的问题,例如库存管理、销售预测、顾客个性化推荐等。明确的步骤…

2024高级程序语言设计作业5

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2024C/homework/13298 学号:102300123 姓名:鲁申如

【国产化替换】信创操作系统:银河麒麟桌面操作系统V10SP1-2403-X86上安装和使用Wireshark网络协议分析器的详细步骤

https://mp.weixin.qq.com/s/cDPN024RaavRcyedGtKYGg 信创操作系统:银河麒麟桌面操作系统V10SP1-2403-X86上安装和使用Wireshark网络协议分析器的详细步骤原创 易联无界一、引言 1.1 文档概述 Wireshark 适用于所有主流 Linux 发行版,是一款自由开源的网络协议分析器,通过使…

补题03——牛客

1.https://ac.nowcoder.com/acm/contest/93218/C 我的方法便是举例,找规律,最后代码如下: from math import * for _ in range(int(input())): n = int(input()) if n <= 2: print(1) else: print(2**int((ceil(log(n,2))))) 解释一下为什么数字为8时还是8呢?因为1000只能…

用“积佬”的眼睛看世界:高考中的分析学

本文是一个系列,计划长期更新(在笔者健在的前提下)。 本文收录较难的高考习题,并引入一些略微超纲的思维方法,以求更为自然地解决问题,而不去回避其数学本质。 未来会将其整理为三个板块:数列极限、级数、积分。【例 1】已知数列 \(\{a_n\}\) 满足递推关系 \(a_1=1,\ a_…

202.10.25(JDBC)

通过java代码操作数据库 JDBC的简介

测试开发通关秘籍四: 彻底搞懂实例方法,类方法和静态方法

热爱技术的小牛 测试开发通关秘籍四: 彻底搞懂实例方法,类方法和静态方法 在 Python 中,实例测试开发通关秘籍四: 彻底搞懂实例方法,类方法和静态方法 在 Python 中,实例方法、静态方法和类方法是定义在类中的三种方法类型。它们之间的主要区别在于它们如何访问类的属性和…

20222403 2024-2025-1 《网络与系统攻防技术》实验四实验报告

1. 实验内容 一、恶意代码文件类型标识、脱壳与字符串提取 对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下: (1)使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具; (2)使用…

高级语言程序设计第五次个人作业(102400106 刘鑫语)

2024高级语言程序设计:https://edu.cnblogs.com/campus/fzu/2024C 高级语言程序设计课程第五次个人作业:https://edu.cnblogs.com/campus/fzu/2024C/homework/13298 学号:102400106 姓名:刘鑫语 第八章 尝试了很多次都不能以EOF终止,换成了& 8.1 8.2知道了\040\为八进制转…

c#-设计模式之观察者模式

c#-设计模式之观察者模式 概念 指多个对象之间存在一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。该模式又称为包括发布-订阅(Publish/Subscribe)模式,该模式是行为型模式。 结构图角色Subject(观察目标):观察目标是指被观…