热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【go语言】Goroutines并发模式(一)

【go语言】Goroutines并发模式(一),Go语言社区,Golang程序员人脉社



2019独角兽企业重金招聘Python工程师标准>>> hot3.png



前言


由于前一阶段实习中接到的项目的告一段落,不知不觉便多出了许多空余的时间,于是就想总结一下最近因为个人兴趣而学习的一些东西。从这篇文章开始以及后面陆续的几篇关于GO语言的文章,均是博主最近对GO语言学习过程中的一些感悟、总结,类似于学习笔记的东西。记录下来并整理成博客一为对学习的知识做一个整理,二为分享出来给大家(因为国内关于GO语言的中文资料比较少),由于博主能力和知识有限,难免有所靡误,还望勘正。


由于Go最近一系列出色的表现,从一开始Go便紧紧地吸引住了我的眼球。类似于Erlang、Scala等语言,Go也是天生为并发而设计的语言,Go有着许多在原生层面对并发编程进行支持的优秀特性,比如大名鼎鼎的Goroutines、Channels、Select等原生特性。那么废话不多说,这一篇主要是对GO语言中的并发编程模式做一个粗略的归纳总结,文中示例参考自golang conference中的一些演讲和博客,涉及到的Go语言的语法知识细节将予以略去。Go语言语法请参考http://golang.org/




几点强调之处


1. 并发而非并行


首先我们要明确两个名词:并发(Concurrency)、并行(Parallelism)。这两个词可能大家经常搞混淆,因为这两个词所标书的意思太过相近,但是前者更加偏向于设计(Design),而后者更加偏向于结构(Structure)。


  • 如果你有只有一个CPU,那么你的程序可以是并发的,但一定不是并行的

  • 一个良好的并发程序并非一定是并行的

  • 并行是一种物理状态,而并发是一种设计思想、程序的内部结构

  • 多处理器才有可能达到并发的物理状态

2. 什么是Goroutines


Goroutine是一个通过go关键字起起来的独立的执行某个function的过程,它拥有独立的可以自行管理的调用栈。


  • goroutine非常廉价,你可以拥有几千甚至上万的goroutines

  • goroutine不是thread

  • 一个thread之下可能有上千的goroutines

  • 你可以把goroutine理解为廉价的thread



让我们从几个例子开始


  • 一个很无聊的函数

func boring(msg string) {
    for i := 0; ; i++ {
        fmt.Println(msg, i)
        time.Sleep(time.Second)
    }
}

显而易见,这个函数永不停歇的打印msg字符串,并且循环中间会sleep一秒钟,接下来让我们不断改进这个函数。




  • 嗯哼,稍微不那么无聊一点了

func boring(msg string) {
    for i := 0; ; i++ {
        fmt.Println(msg, i)
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
    }
}

我们看到这个函数不再是sleep固定的时间,而是rand出一个随机的duration。这样,可以让我们的这个无聊的函数稍微不可预期一点。


  • 让我们把它run起来!~Let's go!

func main() {
    boring("boring!")
}
func boring(msg string) {
    for i := 0; ; i++ {
        fmt.Println(msg, i)
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
    }
}

好了,无聊的函数跑起来了~~但是,目前我们还没有用到Goroutines的特性


  • 让函数Go起来!

package main
import (
    "fmt"
    "math/rand"
    "time"
)
func main() {
go boring("boring!")
}

程序的输出为:


[no output]
Program exited.

纳尼??!!奇怪啊,为什么程序没有输出捏?其实真相是,main函数在开启boring方法的新的goroutine之后,没有等待boring方法调用fmt.Println就急急忙忙返回退出了。当main退出之时,我们的程序自然而然也就退出了。


  • 让我们等一下TA吧

func main() {
go boring("boring!")
fmt.Println("I'm listening.")
time.Sleep(2 * time.Second)
fmt.Println("You're boring; I'm leaving.")
}

现在我们就可以在主程序退出之前看到boring函数输出的message了。但是等等,我们现在还只是一个goroutine,还没有涉及到真正意义上的并发。


  • 使用channels!

让我们先来看一个简单的使用channels进行同步的例子


var syn chan int = make(chan int)
func foo(){
for(i := 0; i < 5; i++){
fmt.Println("i am running!")
}
syn <- 1
}
func main(){
go foo()
<- syn
}

很简单吧,通过使用通道syn,可以进行简单的同步。这样,在main函数退出之间首先会在读取syn处阻塞,除非foo向syn写入数据。


  • 让boring和main成为好基友

func boring(msg string, c chan string) {
   for i := 0; ; i++ {
        c <- fmt.Sprintf("%s %d", msg, i)
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
func main() {
c := make(chan string)
go boring("boring!", c)
  for i := 0; i < 5; i++ {
     fmt.Printf("You say: %qn", <-c)
    }
    fmt.Println("You're boring; I'm leaving.")
}

我们通过channel将main和boring联系起来,从而让本来毫无关系的他们能够自然地交流,从而知晓彼此的状态。上面程序便是通过channel来进行的同步。当main函数执行 "<- c"时会发生阻塞,除非boring中执行"c <- fmt.Sprintf("%s %d", msg, i)"向通道中写入数据才会解除阻塞。由此观之,即针对同一个channel,sender和receiver必须要一个读一个写才能使得channel畅通不阻塞。如此一来,便可以通过channel进行交流和同步。

















转载于:https://my.oschina.net/nyankosama/blog/271319




推荐阅读
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 数组的排序:数组本身有Arrays类中的sort()方法,这里写几种常见的排序方法。(1)冒泡排序法publicstaticvoidmain(String[]args ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
author-avatar
mobiledu2502908043
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有