文档章节

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

何良瑞Nyanko君
 何良瑞Nyanko君
发布于 2014/05/31 18:45
字数 1395
阅读 1558
收藏 9

前言

由于前一阶段实习中接到的项目的告一段落,不知不觉便多出了许多空余的时间,于是就想总结一下最近因为个人兴趣而学习的一些东西。从这篇文章开始以及后面陆续的几篇关于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: %q\n", <-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进行交流和同步。






© 著作权归作者所有

共有 人打赏支持
何良瑞Nyanko君
粉丝 7
博文 5
码字总数 5913
作品 1
南京
【go语言】Goroutines 并发模式(二)

前言 在Goroutines 并发模式(一)中,我们简单地通过boring函数的例子来粗略地阐述了通过channels来和goroutines交流的方法。在本篇中,我将从pattern的方向出发,通过对boring函数的例子进...

何良瑞Nyanko君
2014/05/31
0
2
golang入门学习笔记(二)

作者: 一字马胡 转载标志 【2017-11-22】 更新日志 日期 更新内容 备注 2017-11-22 新建文章 go语言入门学习笔记(二) golang入门学习笔记系列 golang入门学习笔记(一) interface for go...

一字马胡
2017/11/22
0
0
Go中文分词--Sego

词典用双数组trie(Double-Array Trie)实现, 分词器算法为基于词频的最短路径加动态规划。 支持普通和搜索引擎两种分词模式,支持用户词典、词性标注,可运行JSON RPC服务。 分词速度单线程...

匿名
2016/04/18
417
0
Go语言并发与并行学习笔记(二)

目录(?) [-] Go语言的并发和并行 goroutine是在并行吗 并行和并发 真正的并行 一个小问题 runtime调度器 总结 开启多核的实验 Go语言的并发和并行 不知道你有没有注意到一个现象,还是这段代...

nop4ss
2015/07/23
64
0
Go 语法速览与实践清单(下-V0.5)

上篇3D 视角看 Go 并发编程 视频链接+PPT资料如下 链接:https://pan.baidu.com/s/1yaZp7ITQqo01OBuGGlzwQ 密码:f3cm Embedding Go 语言中并没有子类继承这样的概念,而是通过嵌入(Embeddi...

xjtuhit
04/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MySQL面试题集锦

什么是数据库索引?索引有哪几种类型?什么是最左前缀原则?索引算法有哪些?有什么区别? 索引是对数据库表中一列或多列的值进行排序的一种结构。一个非常恰当的比喻就是书的目录页与书的正...

老道士
19分钟前
0
0
使用 LogStash 归集日志

elastic 官网: https://www.elastic.co/ 为了便于集中查看多台主机的业务日志,使用 Filebeat, Redis, Logstash的方式进行收集: (1) Filebeat 监控日志文件的变化, 将新增部分写入redis中, 每...

ouhoo
22分钟前
0
0
java序列化(六) - protostuff序列化

添加依赖 <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.5.9</version> </de......

晨猫
29分钟前
0
0
Ceph学习笔记1-Mimic版本多节点部署

特别说明: 本方法也可以用于单节点部署,只部署一个Monitor(只是会形成单点故障而已),最低要求是使用两个分区创建2个OSD(因为默认最小副本是2);如果不需要使用CephFS,则可以不部署M...

LastRitter
31分钟前
0
0
923. 3Sum With Multiplicity - LeetCode

Question 923. 3Sum With Multiplicity Solution 题目大意: 给一个int数组A和一个目标值target,求满足下面两个条件的组合个数,其中i,j,k分别为数组的索引 i<j<k target = A[i] + A[j] + A[k...

yysue
33分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部