关键词:
Go语言从入门到规范-3.1、Go并发
文章目录
1、Go程
Go 程(goroutine)是由 Go 运行时管理的轻量级线程。
go f(x, y, z)
会启动一个新的 Go 程并执行
f(x, y, z)
f
, x
, y
和 z
的求值发生在当前的 Go 程中,而 f
的执行发生在新的 Go 程中。
Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。sync 包提供了这种能力,不过在 Go 中并不经常用到,因为还有其它的办法(见下一页)。
package main
import (
"fmt"
"time"
)
func say(s string)
for i := 0; i < 5; i++
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
func main()
go say("world")
say("hello")
结果:
world
hello
hello
world
world
hello
hello
world
world
hello
2、信道
信道是带有类型的管道,你可以通过它用信道操作符 <-
来发送或者接收值。
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
func main()
s := []int7, 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
fmt.Println(x, y, x+y)
结果:
-5 17 12
3、带缓存的信道
信道可以是 带缓冲的。将缓冲长度作为第二个参数提供给 make
来初始化一个带缓冲的信道:
ch := make(chan int, 100)
仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
修改示例填满缓冲区,然后看看会发生什么。
package main
import "fmt"
func main()
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
结果:
1
2
4、range和close
发送者可通过 close
关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完
v, ok := <-ch
之后 ok
会被设置为 false
。
循环 for i := range c
会不断从信道接收值,直到它被关闭。
注意: 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
还要注意: 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range
循环。
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)
结果:
0
1
1
2
3
5
8
13
21
34
5、select语句
select
语句使一个 Go 程可以等待多个通信操作。
select
会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
package main
import "fmt"
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
func main()
c := make(chan int)
quit := make(chan int)
go func()
for i := 0; i < 10; i++
fmt.Println(<-c)
quit <- 0
()
fibonacci(c, quit)
结果:
0
1
1
2
3
5
8
13
21
34
quit
6、默认选择
当 select
中的其它分支都没有准备好时,default
分支就会执行。
为了在尝试发送或者接收时不发生阻塞,可使用 default
分支:
select
case i := <-c:
// 使用 i
default:
// 从 c 中接收会阻塞时执行
package main
import (
"fmt"
"time"
)
func main()
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for
select
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
结果:
.
.
tick.
.
.
tick.
.
.
tick.
.
.
tick.
.
.
tick.
BOOM!
7、互斥
我们已经看到信道非常适合在各个 Go 程间进行通信。
但是如果我们并不需要通信呢?比如说,若我们只是想保证每次只有一个 Go 程能够访问一个共享的变量,从而避免冲突?
这里涉及的概念叫做 互斥(mutualexclusion)* ,我们通常使用 互斥锁(Mutex) 这一数据结构来提供这种机制。
Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:
-
Lock
-
Unlock
我们可以通过在代码前调用 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()
defer c.mux.Unlock()
return c.v[key]
func main()
c := SafeCounterv: make(map[string]int)
for i := 0; i < 1000; i++
go c.Inc("somekey")
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
结果:
1000
go语言语法入门
引言GoGo语言是谷歌2009发布的编程语言,它是一种并发的、带垃圾回收的、快速编译的语言。它结合了解释型语言的游刃有余,动态类型语言的开发效率,以及静态类型的安全性。它也打算成为现代的,支持网络与多核计算的语... 查看详情
go语言教程哪里有?go从入门到精通系列视频3.1对称加密算法
3.1.1 对称加密的概述对称加密(也叫私钥加密算法)指加密和解密使用相同密钥的加密算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们... 查看详情
go语言并发与并行学习笔记
...正并行的线程的追求,就不会认识到Go有多么的迷人。Go语言从语言层面上就支持了并发,这与其他语言大不一样,不像以前我们要用Thread库来新建线程,还要用线程安全的队列库来共享数据。以下是我入门的学习笔记。首先,... 查看详情
跟我一起从入门到精通go(代码片段)
go安装环境下载go并运行第一个go程序go25个关键字breakcasechanconstcontinuedefaultdeferelsefallthroughforfuncgogotoifimportinterfacemappackagerangereturnselectstructswitchtypevar并发编程之同步原语sync包sync.Mutex在共享资源上不能同时访问sync.RW 查看详情
gogin从入门到精通-哪有那么多为什么
系列文章目录文章目录系列文章目录一、为什么是Go语言?二、为什么是Gin框架?三、学习计划安排(大概)四、为什么学习?一、为什么是Go语言?如果你是从事PHP、Python相关开发的人员,欢迎你学习Go语言。Go编程语言是一个... 查看详情
gogin从入门到精通-哪有那么多为什么
系列文章目录文章目录系列文章目录一、为什么是Go语言?二、为什么是Gin框架?三、学习计划安排(大概)四、为什么学习?一、为什么是Go语言?如果你是从事PHP、Python相关开发的人员,欢迎你学习Go语言。Go编程语言是一个... 查看详情
go并发
Go语言从语言层面上就支持了并发,这与其他语言大不一样,不像以前我们要用Thread库来新建线程,还要用线程安全的队列库来共享数据。以下是我入门的学习笔记。首先,并行!=并发,两者是不同的,可以参考:http://concur.rspace.goo... 查看详情
七天入门go语言通道&goroutine|第四天并发编程(代码片段)
1.前言在go社区有这样一句话不要通过共享内存来通信,而是通过通信来共享内存。go官方是建议使用管道通信的方式来进行并发。通道是用于协程间交流的通信载体。严格地来说,通道就是数据传输的管道,数据通过... 查看详情
go语言从入门到放弃(代码片段)
...packagemain 定义了该程序的包名为main,main的功能同其它语言一样,都代表着程序的主 查看详情
go语言从入门到放弃数据类型(代码片段)
本章主要介绍Go语言的数据类型布尔(bool)布尔指对或者错,也就是说bool只有两个值,True或False两个类型相同的值可以使用比较运算符来得出一个布尔值当两个值是完全相同的情况下会返回True,否则返回False。packagemainvarsumaint=11v... 查看详情
go系列教程——第20部分:并发入门
Go是并发式语言,而不是并行式语言。在讨论Go如何处理并发之前,我们必须理解何为并发,以及并发与并行的区别。并发是什么?并发是指立即处理多个任务的能力。一个例子就能很好地说明这一点。我们可以想象一个人正在... 查看详情
go语言并发编程简单入门
并发是逻辑上具备同时处理多个任务的能力,并行是在物理上的同一时刻执行多个并发任务。在单核处理器上,它们可以使用间隔的方式切换执行,并行则是依赖多核处理器的物理设备的特性。并行计算是并发设计的最理想模式... 查看详情
go语言入门(代码片段)
GO语言耳闻已久的Go语言,今天入门了解一下。Go之所以火爆的原因之一是,拥有CoreOS,Kubernetes(Google),InfluxDB知名大客户,另外Docker也是Go语言所编写。Go语言的功能特性(所有这些特性,以后慢慢学习了解)并发支持简单性... 查看详情
融云开发漫谈:你是否了解go语言并发编程的第一要义?
2007年诞生的Go语言,凭借其近C的执行性能和近解析型语言的开发效率,以及近乎完美的编译速度,席卷全球。Go语言相关书籍也如雨后春笋般涌现,前不久,一本名为《Go语言并发之道》的书籍被翻译引进国内,并迅速引起广泛... 查看详情
09.go语言并发(代码片段)
Go语言并发并发指在同一时间内可以执行多个任务。并发编程含义比较广泛,包含多线程编程、多进程编程及分布式程序等。本章讲解的并发含义属于多线程编程。Go语言通过编译器运行时(runtime),从语言上支持了并发的特性... 查看详情
go程序开发快速入门(代码片段)
...量名和注释,确保代码易于理解和维护。2、错误处理:Go语言有很好的错误处理机制,应该合理地处理错误,以便于排除错误。3、内存管理:Go语言自动管理内存,但是如果存在大量的内存分配和垃圾回收,会影响程序性能,因... 查看详情
三十分钟入门基础go(java小子版)
作者:京东科技韩国凯前言Go语言定义Go(又称Golang)是Google的RobertGriesemer,RobPike及KenThompson开发的一种静态、强类型、编译型语言。Go语言语法与C相近,但功能上有:内存安全,GC,结构形态及CSP-style并发计算。适用范... 查看详情
go语言代码规范指南(代码片段)
...统一性。本规范将从命名规范,注释规范,代码风格和Go语言提供的常用的工具这几个方面做一个说明。该规范参考了go语言官方代码的风格制定。一、命名规范命名是代码规范中很重要的一部分,统一的命名规则有利于提高的... 查看详情