美文网首页GoGo语言实践
Go并发编程(下)

Go并发编程(下)

作者: 帅气的昵称都有人用了 | 来源:发表于2019-04-08 16:54 被阅读3次

通道

上一次我们讲到了协程,也就是goroutine,并且我们知道了除了goroutine的方式之外,还有一种被称为消息通道机制的通信方式,也就是今天我们要介绍的channel

\color{blue}{channel是Go语言在语言级别提供的goroutine间的通信方式,可以使用channel在两个或多个goroutine之间传递消息。}

channel是进程内的通信方式,因此我们可以传递包括指针在内的各种参数,但如果涉及到跨进程通信时,最好我们要考虑一下使用分布式系统的方法来进行解决,比如使用比较常见的SocketHTTP等通信协议。
我们在了解channel之前,还需要记住一句话:

\color{orange}{channel是类型相关的,一个channel只能传递一种类型的值,而且这个类型需要在声明channel时制定}
我们先来看一个例子:

package main

import "fmt"

var count int = 0

func Count(ch chan int) {
    ch <- count
    count++
    fmt.Println("Counting...", count)
}

func main() {
    chs := make([]chan int, 10)
    for i := 0; i < 10; i++ {
        chs[i] = make(chan int)
        go Count(chs[i])
    }
    for _, ch := range chs {
        <- ch
    }
}
/** The result is:
Counting...0
Counting...1
Counting...2
Counting...3
Counting...4
Counting...5
Counting...6
Counting...7
Counting...8
Counting...9
Counting...10
 */

我们在这个例子当中定义了一个包含10个channel的数组,并且把数组中的每个channel分配个10个不同的goroutine,通过全局变量count实现依次输出1-10的操作,在所有goroutine启动完成后,通过<-ch语句从10个channel中依次读取数据,得到如上结果。
其实,在对应的channel写入数据之前,这个读取数据的操作是阻塞的,这样一来,就可以使用channel实现类似的功能了。也进而保证了所有goroutine在完成后主函数才能返回。


基本语法

接下来我们先了解一些channel的基本语法:
一般channel的声明形式为:

var chanName chan ElemType

比如我们声明一个map,元素是bool型的channel:

var m map[string] chan bool

定义一个channel也是很简单的:

ch := make(chan int)

在声明之后,就设计到channel的基本操作了,当然最主要的操作就是写入读出了。
将数据写入至channel的语法为:

ch <- value

在这里要注意,向channel写入数据通常会导致程序的阻塞,知道有其他gotoutine从这个channel中读取数据。读取语法为:

value := <-ch

select 关键字

Go语言在语言级别是直接支持select的关键字的,用于处理异步I/O问题。
其实selectswitch是非常类似的,但select具有更多的限制,其中最大的一个限制就是每个case语句中必须是一个I/O操作,大致的结构如下:

select {
case <- chan1:  //如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:  //如果成功向chan2写入数据,则进行该case处理语句
default:  //如果上面都没有成功,就执行default中的内容    
} 

从上述的内容可以看出,select并不像switch,后面是不带判断条件的,而是直接去查看case语句。


缓冲机制

前面的channel都是不带缓冲的,但是在面对大量的数据的时候,单个传递的channel(不带缓冲)就有些不太合适了,接下来介绍如何给channel带上缓冲,从而达到消息队列的效果。
创建带缓冲的channel

c := make(chan int, 1024)

即在调用make()时将缓冲区大小作为第二个参数传入即可。
这样一来,写入方可以一直往channel里面写,在缓冲区被填完之前都不会阻塞。


超时和计时器

在前面的介绍与讲述中,并没有提及有关错误处理的任何问题,而这个问题显然是不能被忽略的,和许多地方一样,最需要考虑的一个问题就是超时的问题。

\color{orange}{如果不正确处理可能出现超时问题的情况,很可能会导致整个goroutine锁死}

Go语言并没有提供直接处理超时的机制,但我们可以使用select机制,通过使用select,可以很方便的解决超时问题。

timeout := make(chan bool, 1)
go func() {
    time.Sleep(1e9)
    timeout <- true
    }() 
    select {
    case <- ch: // 从ch中读取到数据
    case <- timeout: // 一直没有从ch中读取到数据,但从timeout中读取到了数据
}

channel的传递

Go语言中channel本身也是一个原生类型,与map之类的类型地位一样,因此channel本身在定义后也可以通过channel来传递。我们可以使用这一特性来实现Linux/UNIX中非常常见的管道pipe特性。

/** 首先先限定基本的数据结构 */
type PipeData struct {
    value int
    handler func(int) int
    next chan int
}

/** 一个十分简单的函数 */
func handle(queue chan *PipeData) {
    for data := range queue {
        data.next <- data.handler(data.value)
    }
}

单向channel

顾名思义,单向channel是只能用于发送或者接受数据的。而且定义方法也非常简单:

var ch1 chan int // ch1是一个非常正常的channel
var ch2 chan<- float64 // ch2是单向的channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据
/** 类型的强制转换对于channel来说也是可以的*/
ch4 := make(chan int)
ch5 := <-chan int(ch4)
ch6 := chan<- int(ch4)

我们设计单向channel的主要原因就是要保证所有代码都遵循“最小权限原则”

func Parse(ch <-chan int) {
    for value := range ch {
        fmt.Println("The value after parse is", value)
    }
}

在这里例子当中,如果不做其他任何考虑的话,整个程序是只需要进行读操作的,因此我们将channel定义为了单向channel以最大化节约资源。


关闭channel

close(ch)

只需要一个close函数即可实现channel的关闭。
当然,如果我们希望判断一个channel是否已经被关闭,那么我们可以使用多重返回值的方式:

x,ok := <-ch

这个用法与map中的案件获取value的过程比较类似,只需要看第二个bool返回值即可,如果返回值是false则表示ch已经被关闭。


到这里关于Go语言中的并发内容的介绍就基本上结束了,接下来将有什么更加有趣的东西等待着我们呢?我们一同期待~

相关文章

  • Go并发编程(下)

    通道 上一次我们讲到了协程,也就是goroutine,并且我们知道了除了goroutine的方式之外,还有一种被称...

  • Go基础语法(九)

    Go语言并发 Go 是并发式语言,而不是并行式语言。 并发是指立即处理多个任务的能力。 Go 编程语言原生支持并发...

  • Go并发

    Go语言中的并发编程 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很...

  • 瞅一眼就会使用GO的并发编程分享

    [TOC] GO的并发编程分享 之前我们分享了网络编程,今天我们来看看GO的并发编程分享,我们先来看看他是个啥 啥...

  • Go控制程序生命周期

    近期学了go语言的并发编程,掌握了 并发原语-原子锁,互斥锁,通道 并发原理 并发设计模式今天我们就来锻炼一下,如...

  • day08-go.GPM

    当别人到go为什么支持高并发,或者问为什么go本身对并发编程友好?以及go与Java对比的并发对比 正确回答: 在...

  • Go语言简介

    Go语言简介 Go语言设计的初衷 针对其他语言的痛点进行设计并加入并发编程为大数据,微服务,并发而生的通用编程语言...

  • 13 Go并发编程初探

    一、Go并发编程概述 Go以并发性能强大著称,在在语言级别就原生支持,号称能实现百万级并发,并以此独步江湖,本专题...

  • Go 基础

    基础 [TOC] 特性 Go 并发编程采用CSP模型不需要锁,不需要callback并发编程 vs 并行计算 安装...

  • go 并发编程

    在资源有限的情况下,如何最大化的利用有限的资源就是并发,提高并发 goroutine runtime包 chann...

网友评论

    本文标题:Go并发编程(下)

    本文链接:https://www.haomeiwen.com/subject/topyiqtx.html