26|高并发爬虫:模型、控制与冲突检测
郑建勋
你好,我是郑建勋。
上一节课,我们看到了协程和调度器的工作原理,协程的特性决定了我们无法保证协程之间的执行顺序,然而在真正的实践中,协程是无法完全隔离的。它们通常需要完成数据的共享,或者说要完成某种通信,而这又会导致数据的并发安全等新的问题。
所以,如何合理地组织大量的协程,协程之间如何进行通信,协程怎么优雅退出,如何保证并发安全,这些是我们在设计高并发的模型时需要考虑的问题。好的并发模型能够提升程序的性能、扩展性与安全性。
这节课,我们就来看看一些富有表现力的高并发模型。让我们先从并发带来的问题说起。
数据争用
在 Go 语言中,当两个以上协程同时访问相同的内存空间,并且至少有一个写操作时,就可能会出现并发安全问题,这种现象也被叫做数据争用。在下面这个例子中,两个协程共同访问了全局变量 count。这个程序其实是有数据争用的,因为 count 的最终结果是不明确的。
count++ 操作看起来是一条指令,但是对 CPU 来说,需要先读取 count 的值,执行 +1 操作,再将 count 的值写回内存。大部分人期望的操作可能是: R←0 代表读取到 0,w→1 代表写入 count 为 1;协程 1 写入数据 1 后,协程 2 再写入,count 最后的值为 2。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了高并发爬虫模型设计中的关键技术特点,以及如何利用Go语言提供的工具解决并发安全问题。文章首先讨论了并发安全问题的复杂性,以及在Go语言中可能出现的数据争用情况。随后详细介绍了原子锁、互斥锁和读写锁的使用方法,以及Go语言提供的并发控制库,包括sync.WaitGroup、sync.Once、sync.Pool和sync.Cond等工具。通过这些工具,读者可以了解如何在高并发环境中设计爬虫模型,并解决并发安全问题。此外,文章还介绍了Go语言的并发模型,包括通道的使用以及ping-pong模式和fan-in模式的应用场景。这些内容为读者提供了丰富的技术知识,帮助他们更好地理解并发编程中的关键概念和技术应用。 文章还介绍了fan-out模式和pipeline模式,分别描述了多个协程抢夺同一个通道中的数据的场景以及由通道连接的一系列连续的阶段进行计算的模式。此外,还提到了并发检测工具race的使用方法,以及在Go语言中使用锁和通道的时机。 总的来说,本文通过深入讨论高并发爬虫模型设计中的关键技术特点,以及Go语言提供的工具解决并发安全问题,为读者提供了全面的并发编程技术知识,帮助他们更好地应对高并发环境下的编程挑战。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 进阶 · 分布式爬虫实战》,新⼈⾸单¥68
《Go 进阶 · 分布式爬虫实战》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(4)
- 最新
- 精选
- Geek_c16d38func worker(tasksCh <-chan int, wg *sync.WaitGroup) { defer wg.Done() for { task, ok := <-tasksCh if !ok { return } d := time.Duration(task) * time.Millisecond time.Sleep(d) fmt.Println("processing task", task) } } func pool(wg *sync.WaitGroup, workers, tasks int) { tasksCh := make(chan int) for i := 0; i < workers; i++ { go c(tasksCh, wg) } for i := 0; i < tasks; i++ { tasksCh <- i } close(tasksCh) } func main() { var wg sync.WaitGroup wg.Add(36) go pool(&wg, 36, 50) wg.Wait() } 這個go c錯了要修正
作者回复: 👌
2022-12-23归属地:中国台湾2 - Realm数据需要传递的时候用channel; 数据不动的时候,如获取、修改状态,用锁;2022-12-08归属地:浙江5
- konyo最后的例子好难懂啊2023-01-11归属地:江苏
- shuff1efunc pool(wg *sync.WaitGroup, workers, tasks int) { tasksCh := make(chan int) for i := 0; i < workers; i++ { go c(tasksCh, wg) } for i := 0; i < tasks; i++ { tasksCh <- i } close(tasksCh)} go c改成go worker?2022-12-08归属地:北京1
收起评论