进程,线程的概念在操作系统的书上已经有详细的介绍。进程是内存资源管理和cpu调度的执行单元。为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间,但cpu调度的最小单元变成了线程。
那协程又是什么东西,以及与线程的差异性??

协程,可以看作是轻量级的线程。但与线程不同的是,线程的切换是由操作系统控制的,而协程的切换则是由用户控制的。

最早支持协程的程序语言应该是lisp方言scheme里的continuation(续延),续延允许scheme保存任意函数调用的现场,保存起来并重新执行。Lua,C#,python等语言也有自己的协程实现。

go的goroutinue
今天要讲的goroutinue,本质上就是协程。但有两点不同:
1. goroutinue可以实现并行,也就是说,多个协程可以在多个处理器同时跑。而协程同一时刻只能在一个处理器上跑(把宿主语言想象成单线程的就好了)。
2. goroutine之间的通信是通过channel,而协程的通信是通过yield和resume()操作。

在Go里实现goroutine非常简单,只需要在函数的调用前面加关键字go即可,

go doSth()

下面的例子演示,启动10个goroutines分别打印索引。

package main
import (
"fmt"
"time"
)

func main() {
for i:=1;i<10;i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
//暂停一会,保证打印全部结束
time.Sleep(1e9)
}

在分析goroutine执行的随机性和并发性,把goroutine看作是java的守护线程是完全可以的。上面的例子中,启动了10个goroutine,再加上main函数的主goroutine,总共有11个goroutines。由于goroutine类似于”守护线程“,如果主goroutine不等待片刻,可能程序就没有输出打印了。上面的例子输出如下:(输出的索引是完全随机的)

go的channel
在java的世界里,并发主要是靠锁住临界资源(共享内存)来保证同步的。而channel则是goroutinues之间进行通信的利器。

channel可以形象比喻为工厂里的传送带,一头的生产者goroutine往传输带放东西,另一头的消费者goroutinue则从输送带取东西。channel实际上是一个有类型的消息队列,遵循先进先出的特点。

1. channel的操作符号

ch <- ele 表示ele被发送给channel ch;

ele2 <- ch 表示从channel ch取一个值,然后赋给ele2

2. 阻塞式channel

channel默认是没有缓冲区的,也就是说,通信是阻塞的。send操作必须等到有消费者accept才算完成。

举个栗子

package main
import "fmt"

func main() {
ch1 := make(chan int)
go pump(ch1) // pump hangs
fmt.Println(<-ch1) // prints only 0
}

func pump(ch chan int) {
for i:= 0; ; i++ {
ch <- i
}
}

上面代码pump()里的channel在接受到第一个元素后就被阻塞了,直到主goroutinue拿走了数据。最终channel阻塞在接受第二个元素,程序只打印 0

3 带有buff的channel

没有buff的channel只能容纳一个元素,而带有buff的channel则可以非阻塞容纳N个元素。发送数据到buffed channel不会被阻塞,除非channel已满;同样的,从buffed channel取数据也不会被阻塞,除非channel空了。这有点像java的ConcurrentLinkedQueue。

goroutine和channel的应用
结合goroutine和channel,可以模拟出java处理并发情况的若干情景

1. 实现future

package main
import "fmt"
import "time"

func main() {
future := heavyCalculation()
fmt.Println(<-future)
}

func heavyCalculation() (chan int) {

future := make(chan int)
go func() {
//模拟耗时计算
time.Sleep(1e9)
future <- 666
}()

return future
}

2. 实现CountDownLatch

package main
import "fmt"

func main() {
nTask := 5
ch := make(chan int)
for i:=1;i<=nTask;i++ {
go doTask(ch)
}
for i:=1;i<=nTask;i++ {
<-ch
}
fmt.Println("finished all tasks")
}

func doTask(ch chan<- int) {
//doSth...
ch<- 0
}

3. 并发访问对象

Hashtable是线程安全的,意味着多条线程同时操作hashtable对象是不会引起状态不一致的。查看Hashtable源代码可知,其几乎全部方法都添加synchronized关键字,例如put(),remove()操作。在go里,我们可以在对象内部保存一个函数类型的channel,涉及对象状态的操作都放入channel里,对象初始化的时候开启一条goroutinue,不停地执行匿名函数。

package main
import (
"fmt"
"strconv"
"time"
)

type Person struct {
Name string
salary float64
chF chan func()
}
func NewPerson(name string, salary float64) *Person {
p := &Person{name, salary, make(chan func())}
go p.backend()
return p
}
func (p *Person) backend() {
for f := range p.chF {
f()
}
}

func (p *Person) AddSalary(sal float64) {
p.chF <- func() { p.salary += sal } // (ThreadSafe)

// p.salary += sal (NotThreadSafe)
}

func (p *Person) ReduceSalary(sal float64) {
p.chF <- func() { p.salary -= sal } // (ThreadSafe)

// p.salary -= sal (NotThreadSafe)
}

func (p *Person) Salary() float64 {
fChan := make(chan float64)
p.chF <- func() { fChan <- p.salary }
return <-fChan
}
func (p *Person) String() string {
return p.Name + " - salary is: " +
strconv.FormatFloat(p.Salary(), 'f', 2, 64)
}

func main() {
p := NewPerson("Kingston", 8888.8)
fmt.Println(p)
for i:=1;i<=500;i++ {
go func() {
p.AddSalary(1);
}()
}
for i:=1;i<=500;i++ {
go func() {
p.ReduceSalary(1);
}()
}
time.Sleep(3e9)
fmt.Println("After changed:")
fmt.Println(p)
}

4. 生产者消费者模式

每次涉及到并发情景,都喜欢用生产者消费者模式,因为它太经典啦

2个面包师同时生产面包,5个顾客同时取面包(尽管以下例子的打印不能说明并发的真实情况,因为channel的操作和打印的组合不是原子操作,但不影响程序的逻辑)

package main
import (
"fmt"
"time"
)

func main() {
bread := make(chan int,3)
for i:=1;i<=2;i++ {
go produce(bread)
}
for i:=1;i<=5;i++ {
go consume(bread)
}
time.Sleep(1e9)
}

func produce(ch chan<- int) {
for {
ch <- 1
fmt.Println("produce bread")
time.Sleep(100 * time.Millisecond)
}
}

func consume(ch <-chan int) {
for {
<-ch
fmt.Println("take bread")
time.Sleep(200 * time.Millisecond)
}
}

---------------------
作者:littleschemer
来源:CSDN
原文:https://blog.csdn.net/littleschemer/article/details/70232659
版权声明:本文为博主原创文章,转载请附上博文链接!

[转帖]go 的goroutine 以及 channel 的简介.的更多相关文章

  1. TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...

  2. goroutine 加 channel 代替递归调用,突破递归调用的层级限制

    package main import ( "fmt" "github.com/davecgh/go-spew/spew" "github.com/B ...

  3. Go基础--goroutine和channel

    goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...

  4. goroutine和channel

    近期在学习golang的goroutine和channel时候有一些疑惑: 带缓冲的channel和不带缓冲的channel有什么区别? goroutine和主进程的有哪些影响和关系? 多个gorou ...

  5. Go part 8 并发编程,goroutine, channel

    并发 并发是指的多任务,并发编程含义比较广泛,包含多线程.多进程及分布式程序,这里记录的并发是属于多线程编程 Go 从语言层面上支持了并发的特性,通过 goroutine 来完成,goroutine ...

  6. Go开发[八]goroutine和channel

    进程和线程 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. 一个进程可以创 ...

  7. Go--关于 goroutine、channel

    Go--关于 goroutine.channel goroutine 协程是一种轻量化的线程,由Go编译器进行优化. Go协程具有以下特点: 有独立的栈空间 共享程序堆中的空间 调度由用户控制 如果主 ...

  8. go并发之goroutine和channel,并发控制入门篇

    并发的概念及其重要性 这段是简单科普,大佬可以跳过 并发:并发程序指同时进行多个任务的程序.在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行 ...

  9. Golang channel 用法简介

    channel 是 golang 里相当有趣的一个功能,大部分时候 channel 都是和 goroutine 一起配合使用.本文主要介绍 channel 的一些有趣的用法. 通道(channel), ...

随机推荐

  1. 洛谷 P1142 轰炸

    洛谷 P1142 轰炸 题目描述 “我该怎么办?”飞行员klux向你求助. 事实上,klux面对的是一个很简单的问题,但是他实在太菜了. klux要想轰炸某个区域内的一些地方,它们是位于平面上的一些点 ...

  2. CF1111E Tree 树链剖分,DP

    CF1111E Tree 过年了,洛咕还没爬这次的题,先放个CF的链接吧. 补个LG传送门. 对于每个询问点\(x\),设它的祖先即不能和它放在同一个集合中的点的个数为\(f[x]\),设\(dp[i ...

  3. shell命令注意点

    unset 不能删除readonly的变量 实例: #!/bin/bash name="lalala" readonly name unset name 执行结果: line5:u ...

  4. mybatis学习(一)-------XML 映射配置文件详解

    XML 映射配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息.文档的顶层结构如下: configuration 配 ...

  5. ionic生成签名的APK方法总结

    ionic生成签名的apk步骤如下: 1. 在项目目录下运行 ionic build android --release 先生成一个未签名的apk 2. 在项目目录下运行 keytool -genke ...

  6. docker-compose 部署 EFK

    信息: Docker版本($ docker --version):Docker版本18.06.1-ce,版本e68fc7a 系统信息($ cat /etc/centos-release):CentOS ...

  7. 用C链表实现约瑟夫环问题

    问题:设有n个人围成一个圆圈,现从第s个人开始报数,数到第m的人出列,然后从出列的下一个人重新开始报数,数到第m的人再次出列,如此反复,直到所有的人全部出列为止.对于任意给定的n.s.m,求按出列次序 ...

  8. 背景颜色 - bootStrap4常用CSS笔记

    .bg-primary 重要的背景颜色 .bg-success 执行成功背景颜色 .bg-info 信息提示背景颜色 .bg-warning 警告背景颜色 .bg-danger 危险背景颜色 .bg- ...

  9. 纯命令行界面下安装并运行官方Android emulator

    纯命令行界面指没有安装Android studio. 下载sdk-tools 可以根据实际需要下载,不需要FQ(2018-04-07) 下载后只有一个tools目录. 平台 SDK 工具包 大小 SH ...

  10. 线程_synchronized_volatile_ReentranLock

    线程:cpu同时执行多个任务 synchonized   代码块,对象,类 同步方法和非同步方法可以同时执行同步方法可以调用同步方法(重入)脏读:之同步写,不同步读死锁的demo  一个线程先对A加锁 ...