Go语言并发模式:优化独立工作协程的并行执行


Go语言并发模式:优化独立工作协程的并行执行

本教程探讨go语言中如何优化独立工作协程的并行执行。针对传统顺序执行导致并发效率低下的问题,文章提出了一种通过巧妙重排通道操作的解决方案。该模式允许多个独立工作协程同时启动并并行处理数据,并通过通道接收操作实现同步,确保所有工作完成后再进行下一步处理,从而在保持固定协程数量的同时,显著提升系统吞吐量。

在Go语言中,利用协程(goroutine)和通道(channel)实现并发是其核心优势之一。然而,不恰当的通道操作顺序可能导致即使是独立的任务也无法真正并行执行,从而限制了程序的并发能力。本教程将深入探讨如何通过优化通道操作顺序,使得多个独立的工作协程能够高效并行处理数据,同时满足保持固定协程数量的约束。

挑战:独立工作协程的顺序执行

考虑一个常见的场景:一个主协调协程(例如account)需要将接收到的数据分发给多个独立的子工作协程(例如workerA和workerB)进行处理。要求是:

  1. workerA和workerB各自运行在一个独立的协程中,且这些协程数量固定,不随数据项的增加而动态创建。
  2. workerA和workerB对数据的处理是完全独立的,它们之间没有数据依赖,因此可以并行执行。
  3. 只有当所有相关的子工作协程都完成对当前数据项的处理后,主协调协程才能将该数据项传递给下一个阶段。

初始的实现可能如下所示,其中主协调协程account在处理每个数据项时,会先将数据发送给workerA并等待其完成,然后再发送给workerB并等待其完成。这种串行等待的方式,即使workerA和workerB是独立的,也无法实现真正的并行。

package main

import "fmt"

func workerA(work_in_chan <-chan int, work_out_chan chan<- int) {
    for d := range work_in_chan {
        fmt.Println("A ", d)
        // 模拟工作
        work_out_chan <- d
    }
}

func workerB(work_in_chan <-chan int, work_out_chan chan<- int) {
    for d := range work_in_chan {
        fmt.Println("B ", d)
        // 模拟工作
        work_out_chan <- d
    }
}

func account(account_chan <-chan int, final_chan chan<- int) {
    wa_in := make(chan int)
    wa_out := make(chan int)
    wb_in := make(chan int)
    wb_out := make(chan int)

    go workerA(wa_in, wa_out)
    go workerB(wb_in, wb_out)

    for d := range account_chan {
        // 初始实现:串行处理,无法并行
        wa_in <- d
        <-wa_out // 阻塞,等待workerA完成

        wb_in <- d
        <-wb_out // 阻塞,等待workerB完成

        final_chan <- d
    }
}

func main() {
    account_chan := make(chan int, 100)
    final_chan := make(chan int, 100)

    go account(account_chan, final_chan)

    account_chan <- 1
    account_chan <- 2
    account_chan <- 3
    close(account_chan) // 关闭输入通道,以便account协程最终退出

    // 从final_chan接收结果
    for i := 0; i < 3; i++ {
        fmt.Println("Final:", <-final_chan)
    }
    close(final_chan) // 关闭输出通道
}

在上述代码中,account协程在处理每个数据项d时,首先向wa_in发送数据,然后立即阻塞等待wa_out的返回。只有workerA处理完毕并发送到wa_out后,account协程才能继续向wb_in发送数据,并再次阻塞等待wb_out的返回。这种模式导致workerA和workerB无法同时运行,极大地限制了并发性。

解决方案:重排通道操作实现并行

要解决上述问题,关键在于改变主协调协程中通道的发送和接收顺序。既然workerA和workerB是独立的,我们可以先将数据同时发送给它们,让它们并行开始工作,然后统一等待它们全部完成。

优化的实现如下:

package main

import "fmt"

func workerA(work_in_chan <-chan int, work_out_chan chan<- int) {
    for d := range work_in_chan {
        fmt.Println("A processing:", d)
        // 模拟工作,可能耗时
        work_out_chan <- d // 完成后发送信号
    }
    close(work_out_chan) // 当输入通道关闭时,关闭输出通道
}

func workerB(work_in_chan <-chan int, work_out_chan chan<- int) {
    for d := range work_in_chan {
        fmt.Println("B processing:", d)
        // 模拟工作,可能耗时
        work_out_chan <- d // 完成后发送信号
    }
    close(work_out_chan) // 当输入通道关闭时,关闭输出通道
}

func account(account_chan <-chan int, final_chan chan<- int) {
    // 创建用于workerA和workerB的输入输出通道
    // 注意:这里使用无缓冲通道,确保worker在准备好接收前不会阻塞发送
    wa_in := make(chan int)
    wa_out := make(chan int)
    wb_in := make(chan int)
    wb_out := make(chan int)

    // 启动worker协程
    go workerA(wa_in, wa_out)
    go workerB(wb_in, wb_out)

    // 遍历输入数据
    for d := range account_chan {
        // 1. 同时将数据发送给所有工作协程
        // 假设worker协程已准备好接收,此操作是非阻塞的(对于无缓冲通道,worker必须已在接收端等待)
        // 或如果通道有缓冲,则只要缓冲未满,发送就是非阻塞的
        wa_in <- d
        wb_in <- d

        // 2. 阻塞等待所有工作协程完成
        // 接收操作会阻塞,直到对应的worker完成其工作并发送信号
        <-wa_out
        <-wb_out

        // 3. 所有工作完成后,将数据发送到最终通道
        final_chan <- d
    }

    // 当account_chan关闭且所有数据处理完毕后,关闭worker的输入通道
    // 这样worker协程才能从for range循环中退出
    close(wa_in)
    close(wb_in)

    // 等待worker协程完成所有剩余工作并关闭其输出通道
    // 确保在关闭final_chan之前所有数据都已处理
    for range wa_out {} // 消费完所有wa_out中可能剩余的信号
    for range wb_out {} // 消费完所有wb_out中可能剩余的信号

    close(final_chan) // 所有工作完成后关闭最终输出通道
}

func main() {
    account_chan := make(chan int, 100) // 带缓冲的输入通道
    final_chan := make(chan int, 100)   // 带缓冲的输出通道

    go account(account_chan, final_chan)

    // 发送数据
    account_chan <- 1
    account_chan <- 2
    account_chan <- 3
    close(account_chan) // 发送完毕,关闭输入通道

    // 从final_chan接收结果
    for res := range final_chan {
        fmt.Println("Final result:", res)
    }
}

代码分析:

  1. 并行启动工作: wa_in
  2. 同步等待完成:
  3. 顺序不重要: 即使workerA比workerB先完成,或者反之,这种模式都能正确工作。因为account协程会同时等待两个接收操作,无论哪个先完成,它都会继续等待另一个,直到两者都完成为止。

通过这种简单的通道操作重排,我们成功地让两个独立的worker协程实现了真正的并行处理,同时满足了所有数据项必须经过所有worker处理的同步要求,并且保持了固定数量的协程。

关键概念与注意事项

  1. 并发与并行:

    启科网络PHP商城系统 启科网络PHP商城系统

    启科网络商城系统由启科网络技术开发团队完全自主开发,使用国内最流行高效的PHP程序语言,并用小巧的MySql作为数据库服务器,并且使用Smarty引擎来分离网站程序与前端设计代码,让建立的网站可以自由制作个性化的页面。 系统使用标签作为数据调用格式,网站前台开发人员只要简单学习系统标签功能和使用方法,将标签设置在制作的HTML模板中进行对网站数据、内容、信息等的调用,即可建设出美观、个性的网站。

    启科网络PHP商城系统 0 查看详情 启科网络PHP商城系统
    • 并发(Concurrency) 是指程序设计结构能够处理多个任务。Go语言通过协程(goroutines)提供了优秀的并发原语。
    • 并行(Parallelism) 是指多个任务在同一时间点上物理地同时执行。本教程的优化正是为了在多核处理器上实现workerA和workerB的并行执行。
  2. 通道缓冲:

    • 在上述示例中,wa_in、wa_out、wb_in、wb_out通道默认是无缓冲的。这意味着发送操作会阻塞,直到有接收者准备好接收;接收操作会阻塞,直到有发送者发送数据。这种行为保证了严格的同步。
    • 如果将这些通道设置为带缓冲的(例如make(chan int, 1)),则发送操作在缓冲区未满时是非阻塞的。这可以减少协调协程与工作协程之间的紧密耦合,提高吞吐量,但需要注意缓冲区大小的选择,以避免死锁或资源耗尽。
  3. sync.WaitGroup的替代方案:

    • 在当前场景中,workerA和workerB的输出通道(wa_out, wb_out)仅用于发送完成信号,其传输的具体值并不重要。

    • 如果工作协程的输出值确实不需要被主协调协程使用,那么使用sync.WaitGroup可能是一个更简洁、更高效的同步机制。sync.WaitGroup专门用于等待一组协程完成。

    • 使用sync.WaitGroup的伪代码示例:

      // ... (workerA和workerB不再需要work_out_chan,而是接收一个*sync.WaitGroup)
      func workerA(work_in_chan <-chan int, wg *sync.WaitGroup) {
          defer wg.Done() // 在函数退出时通知WaitGroup
          for d := range work_in_chan {
              // ... 处理数据
          }
      }
      
      func account(account_chan <-chan int, final_chan chan<- int) {
          // ...
          var wg sync.WaitGroup
          // ...
          for d := range account_chan {
              wg.Add(2) // 增加计数,表示有两个worker需要完成
              wa_in <- d
              wb_in <- d
              wg.Wait() // 阻塞等待所有worker完成
              final_chan <- d
          }
          // ...
      }
    • sync.WaitGroup的优势在于它更明确地表达了“等待一组任务完成”的意图,并且避免了创建不必要的通道。

  4. 优雅关闭:

    • 在main函数中,通过close(account_chan)来通知account协程不再有新的数据。
    • account协程在for range account_chan循环结束后,需要close(wa_in)和close(wb_in)来通知workerA和workerB不再有新的输入。
    • workerA和workerB在接收通道关闭后,也会退出其for range循环,并close其输出通道。
    • account协程在关闭其输入通道后,需要确保所有worker协程都已完成并关闭其输出通道后,才能安全地关闭final_chan。通过for range wa_out {}和for range wb_out {}来消费完所有可能的剩余信号,确保worker协程完全退出。这确保了整个数据流的完整性和程序的优雅终止。

总结

通过对Go语言中通道操作顺序的细致调整,我们能够有效地将独立的任务从串行执行转变为并行执行,从而充分利用多核处理器的能力,提升程序的整体吞吐量。这种模式的核心思想是:先同时启动所有独立的工作任务(通过非阻塞发送),然后统一等待所有任务完成(通过阻塞接收)。在实际开发中,根据具体需求(是否需要传递结果、同步机制的简洁性等),可以选择使用通道进行同步,或者考虑使用sync.WaitGroup等更专业的同步原语。理解并熟练运用这些并发模式,是编写高性能Go语言应用的关键。

以上就是Go语言并发模式:优化独立工作协程的并行执行的详细内容,更多请关注其它相关文章!


# 都已  # 阿里云esc建设网站  # 连云港营销推广地址在哪里  # 平舆企业网站推广营销  # 网络营销推广平台排名  # 柳州谷歌seo优化报价  # 产品网站推广教程  # seo的专业名词  # 商机互联网站推广  # seo培训渠道  # 虎门个人网站建设  # 未满  # 先将  # go  # 死锁  # 是指  # 器中  # 发送给  # 完成后  # 多核  # 多个  # 同步机制  # ai  # go语言  # 处理器 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: @Team是什么?揭秘团队含义  c++类和对象到底是什么_c++面向对象编程基础  t3出行如何使用微信支付  如何高效地基于键列值映射DataFrame中的多个列  嘴唇干裂起皮怎么办 唇部护理与预防干裂的方法【详解】  抖音评论无法发送如何修复 抖音评论功能操作指南  yy漫画官方网站登录入口_yy漫画在线阅读页面地址  西瓜视频怎么查看访客记录_西瓜视频访客记录查看方法  TikTok笔记文字无法编辑如何解决 TikTok笔记文字编辑优化方法  虫虫漫画绿色安全入口_虫虫漫画绿色安全入口安全看漫画  三角洲行动2025年9月10日摩斯密码分享  掌握Go App Engine项目结构与GOPATH:包管理与导入实践  如何配置VS Code作为您Git操作的默认编辑器  《大周列国志》皇帝律令功能介绍  如何使用 Optional 类型并满足 Pylint 的类型检查  《崩坏:星穹铁道》3.6版本异相仲裁打法及配队推荐  免费占卜在线神算_免费占卜手机神算  百度网盘如何设置上传限额  《微信》视频号原创声明开启方法  Flexbox布局:实现粘性导航与底部页脚的完美结合  Lar*el 关联查询:同时筛选父表与子表数据的高效策略  从HTML表单获取逗号分隔值并转换为NumPy数组进行预测  Golang中的rune与byte类型区别是什么_Golang字符与字节处理详解  《米姆米姆哈》米姆获取及技能攻略  Golang如何使用gRPC拦截器实现日志收集_Golang gRPC拦截器日志收集实践  Win10怎么设置快速启动 Win10开启快速启动设置方法  《下一站江湖2》武器获取方法  传统曲艺莲花落的表演形式是  Excel宏怎么删除_Excel中删除宏的详细操作流程  哔哩哔哩黑名单怎么查看  LINUX怎么查看显卡信息_LINUX查看GPU状态  如何使用CSS Grid实现“大方块左侧,小方块右侧垂直堆叠”的水平布局  Golang如何使用log记录日志信息_Golang log日志记录方法总结  创建快捷方式启动系统保护  todesk如何添加信任设备_todesk信任设备设置教程  夸克浏览器资源嗅探怎么用 夸克浏览器网页资源下载技巧【教程】  Go Template中优雅处理循环最后一项:自定义函数实践  163邮箱网页版入口 163邮箱在线使用  响应式设计中动态背景颜色条的实现指南  圆通快递官方入口不需要登录 在线查询入口快速查询  淘口令快速解析技巧  中大网校app做题记录清除方法  邮政快递寄件查询入口 邮政快递收件查询入口  抖音小程序怎么开通?小程序开通条件是什么?  京东快递包裹信息查询入口 京东快递官方查询平台入口  抖音视频如何添加标题?添加标题有哪些好处?  sublime如何配置PHP开发环境_在sublime中运行与调试PHP代码  vivo手机视频通话美颜怎么设置_vivo视频通话美颜开启方法  《百果园》充值余额方法  Win10如何关闭操作中心通知 Win10免打扰设置全攻略【清爽】 

 2025-10-26

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.