文档章节

Go 之旅五: 并发

好刚
 好刚
发布于 2017/02/28 19:14
字数 1258
阅读 22
收藏 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
23
0
博客导航——一站式搜索(所有博客的汇总帖)

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

qq_26787115
2016/01/08
0
0
F#系列随笔索引

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

长征3号
2017/12/21
0
0
高性能高并发框架--Aurora

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

AbelHalo
2016/04/03
1K
0
WCF技术剖析(卷1)正式出版

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

长平狐
2012/09/04
53
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

异步化,高并发大杀器

今天来聊聊如何让项目异步化的一些事。 在进入正文之前,顺便给大家推荐一个Java架构方面的交流学习群:698581634,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分...

Java大蜗牛
10分钟前
0
0
织梦dedecms安全设置

网站本身做好防范: 1、修改默认后台名。 打开网站根目录,找到[dede],这个文件夹就是后台的路径,可以随意修改,比如修改为[adminbuy],此时后台登陆的路径为:http://www.*****.com/admi...

雨季2014
10分钟前
0
0
完美Python入门基础知识点总结,看完你的Python就完全入门了!

Python标识符 在 Python 里,标识符有字母、数字、下划线组成。 在 Python 中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。 Python 中的标识符是区分大小写的。 以下划线...

糖宝lsh
16分钟前
0
0
Java关于前后台传输中文乱码以及Excel下载乱码解决

针对乱码,一般情况是前后台以及中间件编码格式不同导致.故需要保证编码格式相同即可. 1.前台页面格式指定,这里用的是jsp,故需要将pageEncoding指定为"UTF-8" 2.指定服务器编码格式,比如用的t...

勤奋的蚂蚁
19分钟前
0
0
Flutter组件 - Flexible

Flexible是一个控制Row、Column、Flex等子组件如何布局的组件。 Flexible组件可以使Row、Column、Flex等子组件在主轴方向有填充可用空间的能力(例如,Row在水平方向,Column在垂直方向),但是...

一箭落旄头
22分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部