如何使用 Channel

例子来自于Concurrency is not parallelism

Google Search: A fake framework

v1.0

var (
Web = fakeSearch("web")
Image = fakeSearch("image")
Video = fakeSearch("video")
) type Search func(query string) Result func fakeSearch(kind string) Search {
return func(query string) Result {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
return Result(fmt.Sprintf("%s result for %q\n", kind, query))
}
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
results := Google("golang")
elapsed := time.Since(start)
fmt.Println(results)
fmt.Println(elapsed)
}

关键函数

func Google(query string) (results []Result) {
results = append(results, Web(query))
results = append(results, Image(query))
results = append(results, Video(query))
return
}

Google 2.0

每个 search, 独立并发.

No locks. No condition variables. No callbacks.

func Google(query string) (results []Result) {
c := make(chan Result)
go func() { c <- Web(query) } ()
go func() { c <- Image(query) } ()
go func() { c <- Video(query) } () for i := 0; i < 3; i++ {
result := <-c
results = append(results, result)
}
return
}

Google 2.1

如果某个服务比较慢,怎么办?

No locks. No condition variables. No callbacks.

func Google(query string) (results []Result) {
c := make(chan Result)
go func() { c <- Web(query) } ()
go func() { c <- Image(query) } ()
go func() { c <- Video(query) } () timeout := time.After(80 * time.Millisecond)
for i := 0; i < 3; i++ {
select {
case result := <-c:
results = append(results, result)
case <-timeout:
fmt.Println("timed out")
return
}
}
return
}

Google 3.0 Avoid timeout

No locks. No condition variables. No callbacks.

func First(query string, replicas ...Search) Result {
c := make(chan Result)
searchReplica := func(i int) { c <- replicas[i](query) }
for i := range replicas {
go searchReplica(i)
}
return <-c
}
func Google(query string) (results []Result) {
c := make(chan Result)
go func() { c <- First(query, Web1, Web2) } ()
go func() { c <- First(query, Image1, Image2) } ()
go func() { c <- First(query, Video1, Video2) } ()
timeout := time.After(80 * time.Millisecond)
for i := 0; i < 3; i++ {
select {
case result := <-c:
results = append(results, result)
case <-timeout:
fmt.Println("timed out")
return
}
}
return
}

Google 3.1

上面的例子看起来挺完美,但是存在一个严重的内存泄漏,不知道你看出来没有.

First 中的 searchReplica调用,除了第一个会成功返回以外,其他都不会返回.因为堵塞在 c 上面,从而导致了内存泄漏.

改进也很简单

func First(query string, replicas ...Search) Result {
c := make(chan Result,len(replicas)) //看似多分配了资源,但是很快就会收回
searchReplica := func(i int) { c <- replicas[i](query) }
for i := range replicas {
go searchReplica(i)
}
return <-c
}

经过简单的替换,通过 Go 的并发模型,将一个慢的,顺序执行的,故障敏感的程序改造为了一个快速的,并发的,有冗余的,健壮的程序.

完整的 google 3.1

var (
Web1 = fakeSearch("web")
Web2 = fakeSearch("web")
Image1 = fakeSearch("image")
Image2 = fakeSearch("image")
Video1 = fakeSearch("video")
Video2 = fakeSearch("video")
) type Search func(query string) Result func fakeSearch(kind string) Search {
return func(query string) Result {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
return Result(fmt.Sprintf("%s result for %q\n", kind, query))
}
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
results := Google("golang")
elapsed := time.Since(start)
fmt.Println(results)
fmt.Println(elapsed)
} func First(query string, replicas ...Search) Result {
c := make(chan Result,len(replicas))
searchReplica := func(i int) { c <- replicas[i](query) }
for i := range replicas {
go searchReplica(i)
}
return <-c
}
func Google(query string) (results []Result) {
c := make(chan Result)
go func() { c <- First(query, Web1, Web2) } ()
go func() { c <- First(query, Image1, Image2) } ()
go func() { c <- First(query, Video1, Video2) } ()
timeout := time.After(80 * time.Millisecond)
for i := 0; i < 3; i++ {
select {
case result := <-c:
results = append(results, result)
case <-timeout:
fmt.Println("timed out")
return
}
}
return
}

如何使用 channel的更多相关文章

  1. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

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

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

  3. GO语言之channel

    前言: 初识go语言不到半年,我是一次偶然的机会认识了golang这门语言,看到他简洁的语法风格和强大的语言特性,瞬间有了学习他的兴趣.我是很看好go这样的语言的,一方面因为他有谷歌主推,另一方面他确 ...

  4. Critical: Update Your Windows Secure Channel (cve-2014-6321,MS14-066)

    前言:风雨欲来山满楼,下半年开始各种凶猛的漏洞层出不穷,天下已经不太平,互联网已经进入一个新的台阶 0x01 cve-2014-6321 11月的补丁月,微软请windows的用户吃了顿大餐,发布了1 ...

  5. JAVA NIO Channel

    Basic:   多数通道都是链接到开发的文件描述符的.Channel类提供维持平台独立性的抽象过程.   通道是一种途径,访问和操作操作系统,缓冲区是数据操作点: Channel类继承结构图: 通过 ...

  6. channel Golang

    Golang, 以17个简短代码片段,切底弄懂 channel 基础 (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的 ...

  7. go channel

    channel 是go语言中不同goroutine之间通信一种方法 //定义一个channel c := make(chan bool) //向channel中写入 c <- true //读取 ...

  8. [bigdata] flume file channel CPU消耗比 memory channel高的原因

    https://www.quora.com/Why-does-flume-take-more-resource-CPU-when-file-channel-is-used-compared-to-wh ...

  9. 图解Netty之Pipeline、channel、Context之间的数据流向。

    声明:本文为原创博文,禁止转载.       以下所绘制图形均基于Netty4.0.28版本. 一.connect(outbound类型事件)  当用户调用channel的connect时,会发起一个 ...

  10. go:channel(未完)

    注:1)以下的所有讨论建立在包含整形元素的通道类型之上,即 chan int 2)对于“<-”我的理解是,它可能是一个操作符(接收操作符),也  可能是类型的一部分(如“chan<- in ...

随机推荐

  1. java分割函数split的用法(二)

    package com.b; public class Sysetm { public static void main(String[] args) { String a=new String(&q ...

  2. JS面向对象编程,对象,属性,方法。

    document.write('<script type="text/javascript" src="http://api.map.baidu.com/api?v ...

  3. Linux系统层面标配

    1.MySQL中出现存SWAP,主要会是哪些原因?--物理内存切实不足--numa导致内存分配不均,出现物理内存未使用完,就出现大量swap的使用 2.MySQ中CPU负载很高,是什么原因?给出查找的 ...

  4. Rest之路 - 第一个Rest程序

    在 Eclipse 里新建一个 Dynamic project 将 Jersey 的 jar 包,拷贝到 WebContent -> WEB-INF -> lib 文件夹 Add jars ...

  5. dB2 索引相关

    ALTER TABLE "XXXX"."tableA" PCTFREE 20 ; CREATE INDEX "schema"."X ...

  6. vs2017 android demo

    vs2017自安装以后就没怎么打开过,虽然12出的时候用10,15出的时候用13,17出的时候用15,但我依然坚持不用也装上再说的理念. 1.vs2017开发IOS和Android安装所必不可少的,u ...

  7. 易酷cms本地包含漏洞拿shell

    测试版本:易酷cms2.5 包含一句话:http://localhost/ekucms2.5/?s=my/show/id/{~eval($_POST[x])} 会在网站的/temp/Logs/目录下生 ...

  8. 第十一章 SpringMvc(待续)

    ············

  9. Linux系统SCSI磁盘扫描机制解析及命令实例(转)

    转载请在文首保留原文出处:EMC中文支持论坛 介绍 Linux系统扫描SCSI磁盘有几种方式?Linux新增LUN之后,能否不重启主机就认出设备?如果安装了PowerPath,动态添加/删除LUN的命 ...

  10. latex中如何引用公式

    在使用latex编辑文章时,经常会需要引用公式.图表等等. 如果我们人为地对这些公式.图表进行编号1-2-3-4,然后在文章中使用Eq(1)-Eq(2)-Eq(3)-Eq(4)去引用这些公式,固然是可 ...