文档章节

Go 之旅五: 并发

好刚
 好刚
发布于 2017/02/28 19:14
字数 1258
阅读 22
收藏 0
点赞 0
评论 0

原文链接 http://ironxu.com/713

本文是学习 A Tour of Go (中文参考 Go 之旅中文 ) 整理的笔记。介绍Go 语言线程,信道以及互斥锁的概念和使用方法。

1. Go 线程

$GOPATH/src/go_note/gotour/concurrency/goroutine/goroutine.go 源码如下

/**
 * go 语言线程
 */
package main
import (
    "fmt"
)

func say(s string) {
    for i := 0; i < 2; i++ {
        fmt.Println(i, s)
    }
}


func main() {
    go say("world")
    say("hello")
}

Go 线程(goroutine)是由Go 运行时管理的轻量级线程。

go f(x, y, z)

会启动一个新的 Go 线程程并执行

f(x, y, z)

其中 fxyz 的求值在当前的 Go 线程中进行,而 f 的执行发生在新的 Go 线程中。

Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步,这可以通过使用sync 包或信道实现。

2. 信道

$GOPATH/src/go_note/gotour/concurrency/channel/channel.go 源码如下

/**
 * go 语言信道
 */
package main

import "fmt"

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

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() {
	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)

	// range 与 close
	f := make(chan int, 10) // 创建带有缓冲区的管道
	go fibonacci(cap(f), f)
	for i := range f {
		fmt.Println(i)
	}
}

信道是带有类型的管道,通过信道操作符 <- 来从信道发送或者接收值。

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

“箭头”就是数据流的方向。

信道在使用前必须创建:

ch := make(chan int)

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

2.1 带缓冲的信道

信道可以是带缓冲的, 将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:

ch := make(chan int, 100)

仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。

2.2 range 和 close

发送者可通过 close 关闭一个信道来表示没有需要发送的值。接收者可以通过为接收表达式的第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,该值为 false

v, ok := <-ch
// ok == false

循环 for i := range c 会不断从信道接收值,直到它被关闭。

注意: 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序错误。信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有值需要发送的时候才有必要关闭,如终止一个 range 循环。

3. select 语句

$GOPATH/src/go_note/gotour/concurrency/select/select.go 源码如下:

/**
 * go 语言 select
 */
package main
import (
    "fmt"
)

// go 线程设置c 管道
func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c<-x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        default:
            fmt.Println("    .")
        }
    }
}


func main() {
    c := make(chan int)
    quit := make(chan int)
    // go 线程读取c 管道
    go func() {
        for i:= 0; i < 5; i++{
            fmt.Println(<-c)
        }
        quit<-0
    }()
    fibonacci(c, quit)
}

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

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

select 中的其它分支都没有准备好时,default 分支就会执行。为了在尝试发送或者接收时不发生阻塞,可使用 default 分支

4. sync.Mutex

$GOPATH/src/go_note/gotour/concurrency/mutex/mutex.go 源码如下:

/**
 * go 互斥锁
 */

package main

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

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

// 增加计数器值
func (c *SafeCounter) Inc(key string) {
	c.mux.Lock()
	c.v[key]++
	c.mux.Unlock()
}

// 返回当前计数器值
func (c *SafeCounter) Value(key string) int {
	c.mux.Lock()
	defer c.mux.Unlock() // 在Value 函数返回时解锁
	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")) //1000
}

互斥(mutual exclusion),指一次只有一个 Go 线程能够访问一个共享的变量。一般使用互斥锁(Mutex)来提供这种机制。

Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:

Lock()
Unlock()

通过在代码前调用 Lock() 方法,在代码后调用 Unlock() 方法来保证一段代码的互斥执行。另外可以用 defer 语句来保证互斥锁一定会被解锁。

参考

可以关注我的微博了解更多信息: @刚刚小码农

© 著作权归作者所有

共有 人打赏支持
好刚
粉丝 5
博文 27
码字总数 30189
作品 0
武汉
程序员
《Artech的WCF后续之旅系列》系列技术文章整理收藏

《Artech的WCF后续之旅系列》系列技术文章整理收藏 WCF后续之旅系列来自博客园的Artech,Artech在这个系列里和大家分享其对WCF的一些实现机制、设计原理的理解,以及我在实际的项目开发中的一...

开元中国2015 ⋅ 2015/06/22 ⋅ 0

博客导航——一站式搜索(所有博客的汇总帖)

博客导航——一站式搜索 以后博客肯定会越来越多的,所以这做一个整理,方便各位朋友能快速的锁定自己想要的资源 课程 巧用第三方快速开发Android App 热门第三方SDK及框架 Android Studio G...

qq_26787115 ⋅ 2016/01/08 ⋅ 0

F#系列随笔索引

循着我的Google笔记本,我看到第一条与F#相关的笔记发生在4月7日,到今天刚好6个月整。 为何要学习F#?这是个首当其冲的问题,跟当初“Java还是C#”这样的问题不同,现在是在学习一门新语言。...

长征3号 ⋅ 2017/12/21 ⋅ 0

高性能高并发框架--Aurora

Aurora 是一个建立在 Lightning 之上的高性能高并发框架,底层由Phalocn+Swoole组合驱动,专为丧心病狂的极限性能打造。她适用于需要支持高并发的场景,如API 接口、微服务等。 亮点 Featur...

AbelHalo ⋅ 2016/04/03 ⋅ 0

WCF技术剖析(卷1)正式出版

【内容简介】 本书从WCF的终结点谈起,对终结点的三要素进行了全面而深入的介绍,帮助读者了解地址、绑定和契约的本质。 通过本书对序列化的深入讲解,读者可了解WCF进行操作方法调用与消息之...

长平狐 ⋅ 2012/09/04 ⋅ 0

NDK开发笔记—ndk环境安装及其搭建

ndk环境安装及其搭建 软件下载 链接:pan.baidu.com/s/1cev9FK 密码:7yab 当然其他版本的也可以,建议不要用最新的 注意:执行Javah的时候生成头文件是对Java文件所生成的.class进行处理的命...

codeGoogle ⋅ 2017/09/26 ⋅ 0

使用CCS5.5创建 DSP C6000工程

一、 创建工程 成功安装CCS开发环境之后,运行CodeComposer Studio 5.5.0.exe(默认在开始菜单中)。进入TI DSP集成开发环境。如下图一所示: 图一、CCS集成开发环境 点击菜单栏File->New->...

fanbby ⋅ 03/28 ⋅ 0

linux学习之旅(七)&& 进程间通信之信号

信号机制 1.进程定义 进程就是cpu未完成的工作,是对正在运行的程序过程的抽象。 进程的几个特性: 并发性:任何进程都可以同其他进程一起并发执行 独立性:进程是一个能独立运行的基本单位,...

mon_star ⋅ 2017/07/23 ⋅ 0

唠唠 RDS 那些事 —— RDS on Windows Server 2016 第二篇

上一篇中唠叨了一下什么是 RDS 服务。于是,有人问我现在还有人在用 RDS 吗?我反问为什么没人用呢?对方答曰踩坑的飘过。其实,我是觉得,任何技术在运用到实际项目的时候,都会遇到数不尽的...

loveunicom ⋅ 2017/10/27 ⋅ 0

圣殿骑士博文索引

“圣殿骑士”技术博客,书写自己对技术的理解。天道酬勤、坚持不懈! 圣殿骑士很荣幸入住博客园和51CTO写技术博客,目前主要在一家外资企业从事项目管理、技术架构及企业技术培训工作。由于工...

晨曦之光 ⋅ 2012/03/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 31分钟前 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

版本控制工具

CSV , SVN , GIT ,VSS

颖伙虫 ⋅ 昨天 ⋅ 0

【2018.06.19学习笔记】【linux高级知识 13.1-13.3】

13.1 设置更改root密码 13.2 连接mysql 13.3 mysql常用命令

lgsxp ⋅ 昨天 ⋅ 0

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部