Go语言并发编程简单入门

简介:

并发是逻辑上具备同时处理多个任务的能力,并行是在物理上的同一时刻执行多个并发任务。在单核处理器上,它们可以使用间隔的方式切换执行,并行则是依赖多核处理器的物理设备的特性。

并行计算是并发设计的最理想模式。

多线程或者多进程是并行的基本条件,但是单线程也可以用协程做到并发。尽管协程在单线程上通过主动切换来实现多任务并发,但它也有自己的优势。协程上运行的多个任务本质上是串行执行的,加上可控自主调度,所以并不需要做同步处理。

即使采用多线程也未必就能执行并行。Python就因为GIL限制,默认只能并发而不能并行,所以很多时候转而使用"多进程+协程"架构。

通常情况下,用多线程来实现分布式和负载均衡,减轻单进程垃圾回收压力,用多进程(LWP)抢夺更多的处理器资源,用协程来提高处理器时间片利用率。

关键字go并非执行并发操作,而是创建一个并发任务单元。新建任务呗放置到系统队列中,等待调度器安排合适系统线程去获取执行权。当前流程不会阻塞,不会等待该任务启动,去运行时也不保证并发任务的执行次序。

每个任务单元保存除了函数指针、调用参数外,还会分配执行所需的栈内存空间。相比系统默认的KB级别的线程栈,goroutine自定义栈仅仅需要初始化2KB,所以才可以创建成千上万的并发任务。自定义栈采取按需分配策略,在需要的时候进行扩容,最大能到GB规模。

Wait函数:进程退出时不会等待并发任务结束,可以使用通道阻塞,然后发出退出信号。

除了关闭通道以外,写入数据也可以接触阻塞。

如果要等待多个任务结束,推荐使用sync.WaitGroup。通过设定计数器让每个goroutine在退出前递减,直到递归为0时解除阻塞。尽管WaitGroup.Add函数实现了原子操作,但是建议在goroutine外累加计数器,以避免Add尚未执行,Wait已经退出。

GOMAXPROCS:运行时可能会创建多个线程,但是任何时候仅仅有有限的线程参与并发任务的执行,这个数量和处理器的核心数是相等的。可以使用runtime.GOMAXPROCS函数修改,也可以使用环境变量。

如果参数是小于1的,GOMAXPROCS仅仅返回当前设置的值,不做任何调整。

可以使用runtime.NumCPU来显示CPU的核心数。

LocalStorage:gorontine任务无法设置优先级,无法获取编号,没有局部存储(TLS),甚至连返回值都会被抛弃。如果使用map作为局部存储器,建议期间做同步处理,因为运行时会对其进行并发读写检查。

Gosched:暂停,释放线程去执行其他任务。当前任务被放回队列,等待下次调度是恢复执行。该函数很少被使用,因为运行时会主动像长时间运行(10ms)的任务发出抢占调度。只是当前版本实现算法的问题,不能保证调度总是成功的,所以主动切换还有使用场合。

Goexit:立即终止当前任务,运行时确保所有已经注册延迟调用被执行。该函数不会影响其他并发任务,不会引起panic,自然也就无法捕获。

如果在main.main里调用Goexit,它会等待其他任务结束,然后让其他进程直接崩溃。

无论在那一层,Goexit都可以立即终止整个调用栈,与return不同,标准库函数os.exit可以终止进程,但是不会执行延迟调用。

通道:

Go并未实现严格的并发安全。

Go鼓励使用CSP通道,使用通信来代替内存共享,实现并发安全。

通过消息来避免竞态的模型除了CSP,还有Actor。

作为CSP的核心,通道是显式的,要求操作的双方必须知道数据类型和具体的通道,并不关心另一端操作者的身份和数量。可如果另一端为准备妥当,或者消息未能及时处理,会阻塞当前端。

Actor是透明的,不在乎数据类型及通道,只要知道接受者的信箱就行,默认是异步的方式。

通道只是一个队列。同步模式下,发送和接收方配对,然后直接复制数据给对方。如果配对失败,就会置入等待队列,直到另一方出现后才会被唤醒。

异步模式抢夺的是数据缓冲槽。发送方要求有空槽可供写入,而接收方就会要求缓冲数据可以读取。需求不符合的时候同样加入到等待的队列,直到另一方写入数据或者是腾出空的数据缓冲槽之后才会被唤醒。

通道还会被用作事件通知。

同步模式下必须有配对操作的goroutine操作出现,否则会一直阻塞。

多数时候,异步通道有助于提升功能,减少排队阻塞。

虽然传递指针可以来避免数据的复制,但是必须注意额外的数据并发的安全性。

内置函数cap和len返回缓冲器大小和当前已经缓冲的数量,而对于同步通道则都会返回0,可以根据这个特征判断通道是同步的还是异步的。

可以使用ok-idom或者是range模式进行处理数据。对于循环接收数据range更加简洁一些。及时使用close函数关闭通道引发结束结束通知,否则可能会导致死锁。

通知可以是群体类型的。一次性事件使用close效率会更好一些,没有多余的开销。连续或多样性事件,可以传递不同数据标识实现。还可以使用sync.Cond实现单薄或者是广播时间。

对于close或者是nil通道,发送和接收操作都有响应的规则:

1.向已经关闭通道发送数据,引发panic。

2.从已经关闭接收数据,返回已经缓冲数据或者是零值。

3.无论收发,nil通道都会阻塞。

通道默认是双向的,并不区分发送和接收端。但是某些时候,我们可以限制收发操作的方向来获得更加严谨的操作逻辑。

可是使用make创建单向通道,但是没有任何意义。通常使用类型转换来获取单向通道并赋予操作双方。

如果同时处理多个通道,可以使用select语句,它会随机选择一个可用的通道进行收发操作。

如果等全部通道消息处理结束,可以将已经完成通道设置为nil,这样他就会被阻塞,不会被select选中。

即使是同一个通道也会随机选择case执行。

当所有的通道都不可用时,select会执行default语句,如此可以避免seclect阻塞,但是必须注意处理外层循环,以避免陷入空耗。也可以用default处理一些默认的逻辑。

工厂方法将goroutine和通道绑定。鉴于通道本身就是一个并发安全的队列,可用作ID generator。Pool等用途。

可以使用通道实现信号量。

标准库time提供timeout和tick channel实现。

性能:将发往通道的数据打包,减少传输次数,可以有效提升性能。从实现上来说,通道队列依旧使用锁同步机制,单次获取更多数据(批处理),可以改善因为频繁加锁造成的性能问题。

虽然单词消耗更多的内存,但是性能提升非常明显。如果数组改成切片会造成更多内存分配次数。

通道可能会引发goroutine leak,确切的说是指goroutine处于发送状态或者是接受阻塞状态,但是一直未被唤醒。垃圾回收器并不收集此类资源,导致他们会在等待队列里长久休眠形成资源泄露。

通道并不是用来取代锁的,它们有各自不同的用途,通道倾向于解决逻辑层次的并发处理架构,而锁则是用来保护局部范围内的数据安全。

标准库sync提供互斥和读写锁以及原子操作。

将Mutex作为匿名字段时,相关方法必须实现为pointer-receiver,否则会因为复制导致死锁机制失效。

应将Mutex锁粒度控制在最小范围内,及早释放。

Mutex不支持递归,即便是同一goroutine下也会导致死锁。

建议:

1.对性能要求较高的时候应该避免使用deferUnlock。

2.读写并发时,用RWMutex性能会更好一些。

3.对于单个数据写保护,可以尝试使用原子操作。

4.执行严格测试,尽可能打开数据竞争检查。



 本文转自 棋帅小七 51CTO博客,原文链接:http://blog.51cto.com/xvjunjie/2055128

相关文章
|
19天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
63 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
43 7
|
1月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
1月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
106 71
|
1月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
110 67
|
4天前
|
监控 安全 算法
深度剖析核心科技:Go 语言赋能局域网管理监控软件进阶之旅
在局域网管理监控中,跳表作为一种高效的数据结构,能显著提升流量索引和查询效率。基于Go语言的跳表实现,通过随机化索引层生成、插入和搜索功能,在高并发场景下展现卓越性能。跳表将查询时间复杂度优化至O(log n),助力实时监控异常流量,保障网络安全与稳定。示例代码展示了其在实际应用中的精妙之处。
27 9
|
14天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
47 12
|
1月前
|
存储 Go
go语言中映射
go语言中映射
42 11
|
17天前
|
监控 算法 安全
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
24 0
|
1月前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数