Go 语言入门(三)并发

写在前面

在学习 Go 语言之前,我自己是有一定的 Java 和 C++ 基础的,这篇文章主要是基于A tour of Go编写的,主要是希望记录一下自己的学习历程,加深自己的理解

Go 语言入门(三)并发

Go 程

「Go 程」goroutine:由 Go 运行时管理的轻量级线程

运行「Go 程」很简单,只要执行下面代码:

go f(x, y, z)

就会启动一个新的 Go 程并执行f(x, y, z)fxyz的运算发生在当前的 Go 程中,而f的执行发生在新的 Go 程中。

「Go 程」在相同的地址空间中运行,因此在访问共享的内存时必须进行同步sync包提供了这种能力,不过在 Go 中并不经常用到,我们用得比较多的是信道

信道

「信道」是带有类型的管道,你可以通过它用信道操作符<-来发送或者接收值:

ch <- v     // 将 v 发送至信道 ch
v := <-ch   // 从信道 ch 接受值并赋予 v

可以看到,操作符<-是一个箭头,实际上就是表示数据的流向。它有些类似于队列,对于发送至信道的值是先进先出的。

使用信道

和「切片」以及「映射」一样,在使用信道之前,我们必须先初始化一个信道:

ch := make(chan int)

默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。

以下示例对切片中的数进行求和,将任务分配给两个 Go 程。一旦两个 Go 程完成了它们的计算,它就能算出最终的结果:

package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // 将和送入 c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // 从 c 中接收

    fmt.Println(x, y, x+y)
    // 执行结果: -5 17 12
}

带缓冲的信道

信道是带缓冲的,也就是我们可以指定信道的缓冲区长度:仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。

使用make函数便能够在创建信道的同时指定缓冲区长度:

// 创建一个缓冲区长度为 2 的信道
c := make(chan int, 2)

range 和 close

「发送者」可通过close关闭一个信道来表示没有需要发送的值了。

「接收者」可以通过为接收表达式分配第二个参数来测试信道是否被关闭:

v, ok := <-ch

和「映射」以及接口的「类型断言」相似,如果信道已经关闭,ok会被设为false

作为「接受者」,我们可以使用for i := range c来不断从信道c接受信息,知道它被关闭。

package main

import (
    "fmt"
)

// 向信道中输入斐波那契数列
func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    // 输入完毕后关闭信道
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    // 只有在信道关闭后才会停止循环
    for i := range c {
        fmt.Println(i)
    }
}

注意: 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range 循环。

select 语句

「select 语句」:使一个 Go 程可以等待多个通信操作。

select会阻塞到某个分支可以继续执行为止,这时就会执行该分支;如果多个分支都准备好时,会随机选择一个执行。

下面的例子能够演示并说明一些 select 的使用场景:

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        // 实际上这个 case 一直在不断执行直到 return
        case c <- x:
            x, y = y, x+y
        // 这里时 quit 信道的接收方
        // 直到下面的 go 程中 for 循环执行完毕后,quit 才不为空,才能够执行这个 case
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    // 新建两个信道
    c := make(chan int)
    quit := make(chan int)
    // 启动一个 go 程
    go func() {
        for i := 0; i < 10; i++ {
            // 当 c 缓冲区为空时,接收方(也就是这里)会阻塞
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

上面的例子中,我们可以修改 go 程中 for 循环的循环次数,多试几次就可以明白select语句的执行情况了。

上面我们只为select语句指定了两个 case,实际上我们还可以像switch语句一样为它设置默认值default,当 select 中的其它分支都没有准备好时,default 分支就会执行:

select {
case i := <-c:
    // 使用 i
default:
    // 从 c 中接收会阻塞时执行
}

互斥锁: sync.Mutex

从上面可以看到,通过「信道」我们可以方便的在各个 Go 程之间进行通信。但有时,我们希望同一时间只有一个 Go 程能够访问某个共享的变量,这就是互斥(mutual exclusion),我们通常使用互斥锁(Mutex)这一数据结构来提供这种机制。

Go 标准库中提供了sync.Mutex互斥锁类型及其两个方法:Lock()Unlock()来实现「互斥」。

和 Java 中一样,我们在代码执行前调用Lock(),在代码执行结束后调用Unlock()来保证代码的互斥执行。参加下面代码的Inc()方法

我们可以用defer语句来保证互斥锁一定会被解锁,参见下面的Value()方法:

package main

import (
    "fmt"
    "sync"
    "time"
)

// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    // Lock 之后同一时刻只有一个 goroutine 能访问 c.v
    c.v[key]++
    c.mux.Unlock()
}

// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    // Lock 之后同一时刻只有一个 goroutine 能访问 c.v
    defer c.mux.Unlock()
    return c.v[key]
}

func main() {
    c := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go c.Inc("somekey")
    }

    time.Sleep(time.Second)
    fmt.Println(c.Value("somekey"))
}

原文地址:https://www.cnblogs.com/Bylight/p/11967197.html

时间: 2024-10-23 16:43:20

Go 语言入门(三)并发的相关文章

go语言入门(三)

条件语句 go语言的条件语句结构如下: go语言的条件语句和其他语言类似.简单列举下: 1.if 语句,布尔表达式不需要括号 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } 2.if...else语句 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } else { /* 在布尔表达式为 false 时执行 */ } 3.if语句嵌套 if 布尔表达式 1 { /* 在布尔表达式 1 为 true 时执行 */ if 布尔表达式 2 { /* 在布尔表

C语言编程:一本全面的C语言入门教程(第三版)PDF下载

网盘下载地址:C语言编程:一本全面的C语言入门教程(第三版)PDF下载 – 易分享电子书PDF资源网 作者: (美)Stephen Kochan 出版社: 电子社博文视点资讯有限公司 译者: 张小潘 出版年: 2006年 页数: 543 页 定价: 59.00元 装帧: 平装 ISBN: 9787121007354 内容简介 · · · · · · 本书是极负盛名的C语言入门经典教材,其第一版发行至今已有20年的历史.本书内容详实全面,由浅入深,示例丰富,并在每个章节后面附有部分习题,非常适合读

【南阳OJ分类之语言入门】80题题目+AC代码汇总

声明: 题目部分皆为南阳OJ题目. 代码部分包含AC代码(可能不止一个)和最优代码,大部分都是本人写的,并且大部分为c代码和少部分c++代码and极少java代码,但基本都是c语言知识点,没有太多差别,可能代码有的写的比较丑,毕竟知识有限. 语言入门部分题基本都较为简单,是学习编程入门的很好练习,也是ACM的第一步,入门的最佳方法,望认真对待. 本文由csdn-jtahstu原创,转载请注明出处,欢迎志同道合的朋友一起交流学习.本人QQ:1373758426和csdn博客地址. now begi

Swift语法基础入门三(函数, 闭包)

Swift语法基础入门三(函数, 闭包) 函数: 函数是用来完成特定任务的独立的代码块.你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数 格式: func 函数名称(参数名:参数类型, 参数名:参数类型...) -> 函数返回值 { 函数实现部分 } 没有参数没有返回值 可以写为 ->Void 可以写为 ->() 可以省略 Void.它其实是一个空的元组(tuple),没有任何元素,可以写成() func say() -> V

简单易懂的程序语言入门小册子(9):环境,引入环境

\newcommand{\mt}[1]{\text{#1}} \newcommand{\mE}{\mathcal{E}} \newcommand{\tup}[1]{\left<{#1}\right>} 环境类似于其他语言(C++.JAVA等)的"符号表". 所谓符号表,是一张将变量名与变量代表的内容联系起来的一张表. 不过这里我们抛弃符号表的观点,单纯地从算法角度上引入环境这一概念. 引入环境 通过修改解释器求值过程的算法,可以很自然的引入环境这个概念. 在前面基于文本替换

简单易懂的程序语言入门小册子(6):基于文本替换的解释器,引入continuation

当我写到这里的时候,我自己都吃了一惊. 环境.存储这些比较让人耳熟的还没讲到,continuation先出来了. 维基百科里对continuation的翻译是"延续性". 这翻译看着总有些违和感而且那个条目也令人不忍直视. 总之continuation似乎没有好的中文翻译,仿佛中国的计算机科学里没有continuation这个概念似的. Continuation这个概念相当于过程式语言里的函数调用栈. 它是用于保存"现在没空处理,待会再处理的事"的数据结构. 这样说

1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问

1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象的创建和操作. 二,1985年,被赶出苹果公司的乔帮主成立了Next公司; 三, 1988年,Next计算机公司获得了OC语言的授权,并发展了OC语言库和一个开发环境,1994年,Next计算机公司(同年更名为Next软件公司)和Sun公司针对NEXTSTEP系统联合发布了一个标准规范,名为OPEN

《Go语言入门》如何在Windows下安装Go语言编程环境

概述 本文为Go语言学习入门第一篇,<Go语言入门>如何在Windows下安装Go语言编程环境 . 主要讲Go语言编译环境的安装以及基于Notepad++(Go语言插件.语法高亮)的开发环境配置. 下载安装包 安装包下载地址:https://golang.org/dl/ 这里选择下载Windows版本,点击链接打开的页面可能不会开始下载:地址栏里会显示完整的下载地址,如:https://golang.org/doc/install?download=go1.5.1.windows-amd64.

c语言入门经典(第5版)

文章转载:http://mrcaoyc.blog.163.com/blog/static/23939201520159135915734 文件大小:126MB 文件格式:PDF    [点击下载] C语言入门经典(第5版)  内容简介: C语言是每一位程序员都应该掌握的基础语言.C语言是微软.NET编程中使用的C#语言的基础:C语言是iPhone.iPad和其他苹果设备编程中使用的Objective-C语言的基础:C语言是在很多环境中(包括GNU项目)被广泛使用的C++语言的基础.C语言也是Li