[转帖]go 的goroutine 以及 channel 的简介.
进程,线程的概念在操作系统的书上已经有详细的介绍。进程是内存资源管理和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 的简介.的更多相关文章
- TODO:Go语言goroutine和channel使用
		
TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...
 - goroutine 加 channel 代替递归调用,突破递归调用的层级限制
		
package main import ( "fmt" "github.com/davecgh/go-spew/spew" "github.com/B ...
 - Go基础--goroutine和channel
		
goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...
 - goroutine和channel
		
近期在学习golang的goroutine和channel时候有一些疑惑: 带缓冲的channel和不带缓冲的channel有什么区别? goroutine和主进程的有哪些影响和关系? 多个gorou ...
 - Go part 8 并发编程,goroutine, channel
		
并发 并发是指的多任务,并发编程含义比较广泛,包含多线程.多进程及分布式程序,这里记录的并发是属于多线程编程 Go 从语言层面上支持了并发的特性,通过 goroutine 来完成,goroutine ...
 - Go开发[八]goroutine和channel
		
进程和线程 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. 一个进程可以创 ...
 - Go--关于 goroutine、channel
		
Go--关于 goroutine.channel goroutine 协程是一种轻量化的线程,由Go编译器进行优化. Go协程具有以下特点: 有独立的栈空间 共享程序堆中的空间 调度由用户控制 如果主 ...
 - go并发之goroutine和channel,并发控制入门篇
		
并发的概念及其重要性 这段是简单科普,大佬可以跳过 并发:并发程序指同时进行多个任务的程序.在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行 ...
 - Golang channel 用法简介
		
channel 是 golang 里相当有趣的一个功能,大部分时候 channel 都是和 goroutine 一起配合使用.本文主要介绍 channel 的一些有趣的用法. 通道(channel), ...
 
随机推荐
- [webapp]移动平台各浏览器的分辨率适配
			
搞webapp适配了N多浏览器,记一下各浏览器碰到的需要注意的地方. 目前发现firefox是最难适配的. 1.firefox只有在onload之后才能取到正确的innerwidth值. 2.目前版本 ...
 - 【转】阿里云Linux系统被攻击的处理过程
			
4-22日 19:48分,在等女儿跳舞下课的时候,在“多看”进入大刘等人的<毁灭之城:地球碎块>,读到了“诅咒 3.0”病毒出现的时候,阿里云发来短信“尊敬的用户,您的云服务器x.x.x. ...
 - jenkins 自动上传代码到nexus 私库
			
1.jenkins 项目配置上传 2.jenkins 访问私库下载配置 -X clean install 3.maven 配置文件 /usr/local/maven/conf/settings.xml ...
 - JAVA使用qrcode生成二维码(带logo/不带logo)
			
/** * */ package qrcode; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; i ...
 - 第四节:Windows系统安装时BIOS设置及注意
			
BIOS系统 BIOS是英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统".在IBM PC兼容系统上 ...
 - Kafka发送到分区的message是否是负载均衡的?
			
首先说结论,是负载均衡的.也就是说,现在有一个producer,向一个主题下面的三个分区发送message,没有指定具体要发送给哪个partition, 这种情况,如果是负载均衡的,发送的消息应该均匀 ...
 - Docker持久化存储与数据共享
			
一.Docker持久化数据的方案 基于本地文件系统的Volume:可以在执行docker create或docker run时,通过-v参数将主机的目录作为容器的数据卷.这部分功能便是基于本地文件系统 ...
 - eclipse以MapReduce本地模式运行程序
			
1.准备好所需的文件winutils.exe.hadoop-eclipse-plugin-2.7.3.jar.hadoop-common-2.2.0-bin-master.zip 下载路径:http: ...
 - 论文笔记:分形网络(FractalNet: Ultra-Deep Neural Networks without Residuals)
			
FractalNet: Ultra-Deep Neural Networks without Residuals ICLR 2017 Gustav Larsson, Michael Maire, Gr ...
 - Windows下 搭建redis集群
			
Windows下搭建redis集群教程 一,redis集群介绍 Redis cluster(redis集群)是在版本3.0后才支持的架构,和其他集群一样,都是为了解决单台服务器不够用的情况,也防止了主 ...