Go语言与数据库开发:01-07

简介:

在本节,我们来说一下并发。
并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要

Go语言中的并发程序可以用两种手段来实现。尽管Go对并发的支持是众多强力特性之一,但跟踪调试并发程序还是很困难,在线性程序中
形成的直觉往往还会使我们误入歧途。

. Goroutines

在Go语言中,每一个并发的执行单元叫作一个goroutine。设想这里的一个程序有两个函数,
一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系。一个线性的程
序会先调用其中的一个函数,然后再调用另一个。如果程序中包含多个goroutine,对两个函
数的调用则可能发生在同一时刻。马上就会看到这样的一个程序。
如果你使用过操作系统或者其它语言提供的线程,那么你可以简单地把goroutine类比作一个
线程,这样你就可以写出一些正确的程序了

当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine。新
的goroutine会用go语句来创建。在语法上,go语句是一个普通的函数或方法调用前加上关键
字go。go语句会使其语句中的函数在一个新创建的goroutine中运行。而go语句本身会迅速地
完成。
f() // call f(); wait for it to return
go f() // create a new goroutine that calls f(); don't wait

. Channels

如果说goroutine是Go语音程序的并发体的话,那么channels它们之间的通信机制。一个
channels是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。每
个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数
据的channel一般写为chan int。
使用内置的make函数,我们可以创建一个channel:
ch := make(chan int) // ch has type 'chan int'

和map类似,channel也一个对应make创建的底层数据结构的引用。当我们复制一个channel
或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者何被调用者将引用同
一个channel对象。和其它的引用类型一样,channel的零值也是nil。
两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相通的对象,那
么比较的结果为真。一个channel也可以和nil进行比较。
一个channel有发送和接受两个主要操作,都是通信行为。一个发送语句将一个值从一个
goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是
用 <- 运算符。在发送语句中, <- 运算符分割channel和要发送的值。在接收语句中, <- 运
算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的。
ch <- x // a send statement
x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded

Channel还支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导
致panic异常。对一个已经被close过的channel之行接收操作依然可以接受到之前已经成功发
送的数据;如果channel中已经没有数据的话讲产生一个零值的数据。
使用内置的close函数就可以关闭一个channel:
close(ch)
以最简单方式调用make函数创建的时一个无缓存的channel,但是我们也可以指定第二个整
形参数,对应channel的容量。如果channel的容量大于零,那么该channel就是带缓存的
channel。

ch = make(chan int) // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3

.不带缓存的Channels

一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相
同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以
继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有
另一个goroutine在相同的Channels上执行发送操作。
基于无缓存Channels的发送和接收操作将导致两个goroutine做一次同步操作。因为这个原
因,无缓存Channels有时候也被称为同步Channels。当通过一个无缓存Channels发送数据
时,接收者收到数据发生在唤醒发送者goroutine之前.

在讨论并发编程时,当我们说x事件在y事件之前发生(happens before),我们并不是说x事
件在时间上比y时间更早;我们要表达的意思是要保证在此之前的事件都已经完成了,例如在
此之前的更新某些变量的操作已经完成,你可以放心依赖这些已完成的事件了。
当我们说x事件既不是在y事件之前发生也不是在y事件之后发生,我们就说x事件和y事件是并
发的。这并不是意味着x事件和y事件就一定是同时发生的,我们只是不能确定这两个事件发
生的先后顺序。在下一章中我们将看到,当两个goroutine并发访问了相同的变量时,我们有
必要保证某些事件的执行顺序,以避免出现某些并发问题。

.串联的Channels(Pipeline)

Channels也可以用于将多个goroutine链接在一起,一个Channels的输出作为下一个Channels
的输入。这种串联的Channels就是所谓的管道(pipeline)。
如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多
余的值可接收将是有用的,因为接收者可以停止不必要的接收等待
这可以通过内置的close
函数来关闭channel实现:
close(naturals)
当一个channel被关闭后,再向该channel发送数据将导致panic异常。当一个被关闭的channel
中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零
值。关闭上面例子中的naturals变量对应的channel并不能终止循环,它依然会收到一个永无
休止的零值序列,然后将它们发送给打印者goroutine。
没有办法直接测试一个channel是否被关闭,但是接收操作有一个变体形式:它多接收一个结
果,多接收的第二个结果是一个布尔值ok,ture表示成功从channels接收到值,false表示
channels已经被关闭并且里面没有值可接收。使用这个特性,我们可以修改squarer函数中的
循环代码,当naturals对应的channel被关闭并没有值可接收时跳出循环,并且也关闭squares
对应的channel.

// Squarer
go func() {
for {
x, ok := <-naturals
if !ok {
break // channel was closed and drained
}
squares <- x * x
}
close(squares)
}()

因为上面的语法是笨拙的,而且这种处理模式很场景,因此Go语言的range循环可直接在
channels上面迭代。使用range循环是上面处理模式的简洁语法,它依次从channel接收数
据,当channel被关闭并且没有值可接收时跳出循环。

目录
相关文章
|
10天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
24 7
|
9天前
|
开发框架 Go 计算机视觉
纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架
开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C++ 库,如 OpenCV 或 dlib,但通过 cgo 调用 C 程序会引入巨大的延迟,并在性能方面产生显著的权衡。此外,在许多情况下,在各种平台上安装 OpenCV 是很麻烦的。使用纯Go开发的插件不仅在开发时方便,在项目部署和项目维护也能省很多时间精力。
|
13天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
25 2
|
13天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
14天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
17 3
|
14天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
24 3
|
15天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
15天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
24 3
|
18天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
47 4
|
18天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
59 1