Go 并发实现协程同步的多种解决方法

go 简洁的并发

多核处理器越来越普及。有没有一种简单的办法,能够让我们写的软件释放多核的威力?是有的。随着Golang, Erlang, Scala等为并发设计的程序语言的兴起,新的并发模式逐渐清晰。正如过程式编程和面向对象一样,一个好的编程模式有一个极其简洁的内核,还有在此之上丰富的外延。可以解决现实世界中各种各样的问题。本文以GO语言为例,解释其中内核、外延。

前言

Java 中有一系列的线程同步的方法,go 里面有 goroutine(协程),先看下下面的代码执行的结果是什么呢?

 package main
 import (
   "fmt"
)
 func main() {
  go func() {
    fmt.Println("Goroutine 1")
  }()
  go func() {
    fmt.Println("Goroutine 2")
  }()
}

执行以上代码很可能看不到输出。

因为有可能这两个协程还没得到执行,主协程就已经结束了,而主协程结束时会结束所有其他协程,所以导致代码运行的结果什么都没有。

估计不少新接触 go 的童鞋都会对此郁闷😒,可能会问那么该如何等待主协程中创建的协程执行完毕之后再结束主协程呢?

下面说几种可以解决的方法:

Sleep 一段时间

在 main 方法退出之前 sleep 一段时间就可能会出现结果了,如下代码:

 package main
 import (
   "fmt"
  "time"
 )
 func main() {
   go func() {
    fmt.Println("Goroutine 1")
  }()
  go func() {
    fmt.Println("Goroutine 2")
  }()
  time.Sleep(time.Second * 1) // 睡眠1秒,等待上面两个协程结束
}

这两个简单的协程执行消耗的时间很短的,所以你会发现现在就有结果出现了。

Goroutine 1
Goroutine 2

为什么上面我要说 “可能会出现” ?

因为 sleep 这个时间目前是设置的 1s,如果我这两个协程里面执行了很复杂的逻辑操作(时间大于 1s),那么就会发现依旧也是无结果打印出来的。

那么就可以发现这种方式得到问题所在了:我们无法确定需要睡眠多久

上面那种方式有问题,go 里面其实也可以用管道来实现同步的。

管道实现同步

那么用管道怎么实现同步呢?show code:

 package main
 import (
   "fmt"
 )
 func main() {
   ch := make(chan struct{})
  count := 2 // count 表示活动的协程个数
  go func() {
    fmt.Println("Goroutine 1")
    ch <- struct{}{} // 协程结束,发出信号
  }()
  go func() {
    fmt.Println("Goroutine 2")
    ch <- struct{}{} // 协程结束,发出信号
  }()
  for range ch {
    // 每次从ch中接收数据,表明一个活动的协程结束
    count--
    // 当所有活动的协程都结束时,关闭管道
    if count == 0 {
      close(ch)
    }
  }
}

这种方式是一种比较完美的解决方案, goroutine / channel 它们也是在 go 里面经常搭配在一起的一对。

sync.WaitGroup

其实 go 里面也提供了更简单的方式 —— 使用 sync.WaitGroup。

WaitGroup 顾名思义,就是用来等待一组操作完成的。WaitGroup 内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法:

  • Add() 用来添加计数
  • Done() 用来在操作结束时调用,使计数减一
  • Wait() 用来等待所有的操作结束,即计数变为 0,该函数会在计数不为 0 时等待,在计数为 0 时立即返回

继续 show code:

package main
 import (
   "fmt"
  "sync"
 )
 func main() {
  var wg sync.WaitGroup
  wg.Add(2) // 因为有两个动作,所以增加2个计数
  go func() {
    fmt.Println("Goroutine 1")
    wg.Done() // 操作完成,减少一个计数
  }()
  go func() {
    fmt.Println("Goroutine 2")
    wg.Done() // 操作完成,减少一个计数
  }()
  wg.Wait() // 等待,直到计数为0
}

你会发现也是可以看到运行结果的,是不是发现这种方式是很简单的。

总结

以上所述是小编给大家介绍的Go 并发实现协程同步的多种解决方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • golang 40行代码实现通用协程池

    代码仓库 goroutine-pool golang的协程管理 golang协程机制很方便的解决了并发编程的问题,但是协程并不是没有开销的,所以也需要适当限制一下数量. 不使用协程池的代码(示例代码使用chan实现,代码略啰嗦) func (p *converter) upload(bytes [][]byte) ([]string, error) { ch := make(chan struct{}, 4) wg := &sync.WaitGroup{} wg.Add(len(bytes))

  • python并发编程之多进程、多线程、异步和协程详解

    最近学习python并发,于是对多进程.多线程.异步和协程做了个总结. 一.多线程 多线程就是允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行.即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果. 多线程相当于一个并发(concunrrency)系统.并发系统一般同时执行多个任务.如果多个任务可以共享资源,特别是同时写入某个变量的时候,就需要解决同步的问题,比如多线程火车售票系统:两个指令,一个指令检查票是否卖完

  • python线程、进程和协程详解

    引言 解释器环境:python3.5.1 我们都知道python网络编程的两大必学模块socket和socketserver,其中的socketserver是一个支持IO多路复用和多线程.多进程的模块.一般我们在socketserver服务端代码中都会写这么一句: server = socketserver.ThreadingTCPServer(settings.IP_PORT, MyServer) ThreadingTCPServer这个类是一个支持多线程和TCP协议的socketserver

  • 深入浅析python中的多进程、多线程、协程

    进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. 程序是运行在系统上的具有某种功能的软件,比如说浏览器,音乐播放器等. 每次执行程序的时候,都会完成一定的功能,比如说浏览器帮我们打开网页,为了保证其独立性,就需要一个专门的管理和控制执行程序的数据结构--进程控制块. 进程就是一个程序在一个数据集上的一次动态执行过程. 进程一般由程序.数据集.进程控

  • 深入浅析python 协程与go协程的区别

    进程.线程和协程 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 进程和线程的关系: 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. CPU的最小调度单元是线程不是进程,所以单进程多线程也可以利用多核CPU. 协程的定义: 协程通过在线程中实现调度,避免了陷入内核级别的上下文切换

  • 简单介绍Python的Tornado框架中的协程异步实现原理

    Tornado 4.0 已经发布了很长一段时间了, 新版本广泛的应用了协程(Future)特性. 我们目前已经将 Tornado 升级到最新版本, 而且也大量的使用协程特性. 很长时间没有更新博客, 今天就简单介绍下 Tornado 协程实现原理, Tornado 的协程是基于 Python 的生成器实现的, 所以首先来回顾下生成器. 生成器 Python 的生成器可以保存执行状态 并在下次调用的时候恢复, 通过在函数体内使用 yield 关键字 来创建一个生成器, 通过内置函数 next 或生

  • Go 并发实现协程同步的多种解决方法

    go 简洁的并发 多核处理器越来越普及.有没有一种简单的办法,能够让我们写的软件释放多核的威力?是有的.随着Golang, Erlang, Scala等为并发设计的程序语言的兴起,新的并发模式逐渐清晰.正如过程式编程和面向对象一样,一个好的编程模式有一个极其简洁的内核,还有在此之上丰富的外延.可以解决现实世界中各种各样的问题.本文以GO语言为例,解释其中内核.外延. 前言 Java 中有一系列的线程同步的方法,go 里面有 goroutine(协程),先看下下面的代码执行的结果是什么呢? pac

  • Python并发编程协程(Coroutine)之Gevent详解

    Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporateroutine的缩写,直接翻译为协同的例程,一般我们都简称为协程. 在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程. 进程和协程 下面对比一下进程和协程的相同点和不同点: 相同点: 我们都可以把他们看做是一种执行流,执行流可以挂起,并且后面可以在你挂起的地方恢复执行,这实际上都可以看做是con

  • Go 并发编程协程及调度机制详情

    目录 协程的概念 goroutine 的诞生 使用 goroutine 加快速度 goroutine 的机制原理 前言: 协程(coroutine)是 Go 语言最大的特色之一,goroutine 的实现其实是通过协程. 协程的概念 协程一词最早出现在 1963 年发表的论文中,该论文的作者为美国计算机科学家 Melvin E.Conway.著名的康威定律:“设计系统的架构受制于产生这些设计的组织的沟通结构.” 也是这个作者. 协程是一种用户态的轻量级线程,可以想成一个线程里面可以有多个协程,而

  • java并发请求下数据插入重复问题的解决方法

    目录 前言 分布式锁工具类 在过滤器实现请求拦截 总结 前言 前段时间发现数据库里经常会存在两条相同的用户数据,导致数据查询异常.查了原因,发现前端微信小程序在授权登录时,有时会出现同时发送了两条一模一样的请求(也就是常说的并发).虽然后端代码有做防重复的判断,但是避免不了并发时候的重复性操作.于是就开始考虑并发的解决方案,解决方案有很多,从拦截请求到数据库层面都可以入手. 我们采用了对请求报文生成摘要信息+Redis分布式锁的方案.运行了一段时间,功能很可靠,代码也很简洁.于是上来做下记录以便

  • Go并发:使用sync.WaitGroup实现协程同步方式

    经常看到有人会问如何等待主协程中创建的协程执行完毕之后再结束主协程,例如如下代码: package main import ( "fmt" ) func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }() } 执行以上代码很可能看不到输出,因为有可能这两个协程还没得到执行主协程已经结束了,而主协程结束时会结束所

  • python线程中的同步问题及解决方法

    多线程开发可能遇到的问题 假设两个线程t1和t2都要对num=0进行增1运算,t1和t2都各对num修改1000000次,num的最终的结果应该为2000000.但是由于是多线程访问,有可能出现下面情况: from threading import Thread import time num = 0 def test1(): global num for i in range(1000000): num += 1 print("--test1--num=%d" % num) def

  • $.browser.msie 为空或不是对象问题的多种解决方法

    '$.browser.msie' 为空或不是对象,这个是jQuery错误 出现这个错误,是因为升级了jQuery版本,从1.9以前升级到1.9以后,因为$.browser.msie在1.9以后的jQuery中不存在了,所以报错. jQuery 1.9 移除了 $.browser 的替代方法 $.browser是通过正则表达式来匹配userAgent来判断浏览器版本和种类的.jquery1.3.2版本的文档中已经声明jquery.browser及jquery.browser.version建议弃用

  • jquery中文乱码的多种解决方法

    1.使用$.ajax出现的中文乱码的解决方案: 复制代码 代码如下: var _realname = $("input[name='_searchName']").val();     var termcourseId = '<%=termid%>';     var classId = '<%=classid%>';     var url = "/addressbook/studentListNoPage.do";     //var d

  • iisreset 不支持此接口的多种解决方法分享

    症状:系统是windows 2003,iis一直都是正常的,现在在在命令提示符里使用iisreset命令重启IIS的时候出现"不支持此接口",问题处理办法. 解决办法: 可以尝试使用以下几个方法来解决问题. 方法一.重新注册 iisrstap.dll 文件 运行:regsvr32 iisrstap.dll iisrstap.dll 文件版本:6.0.3790.3959(srv03_sp2_rtm.070216-1710) 描述:IIS Restart API Proxy dll 版权:

  • A标签触发onclick事件而不跳转的多种解决方法

    在web页面开发时,我们经常会遇到下列情况: 1.一个标签仅仅是要触发onclick行为: 2.表现上要有鼠标的pointer指针显示,或者其他类似a标签的视觉效果. 比如执行删除操作时,为了避免误操作,我们要弹出对话框让用户确定是否删除.因此我们经常会用链接<a></a>形式代替<button> 触发onclick事件. 代码如下: 复制代码 代码如下: <script type="text/javascript"> function

随机推荐