Go 进阶 · 分布式爬虫实战
郑建勋
Go 语言技术专家,《Go 语言底层原理剖析》作者
15839 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 58 讲
Go 进阶 · 分布式爬虫实战
15
15
1.0x
00:00/00:00
登录|注册

26|高并发爬虫:模型、控制与冲突检测

你好,我是郑建勋。
上一节课,我们看到了协程和调度器的工作原理,协程的特性决定了我们无法保证协程之间的执行顺序,然而在真正的实践中,协程是无法完全隔离的。它们通常需要完成数据的共享,或者说要完成某种通信,而这又会导致数据的并发安全等新的问题。
所以,如何合理地组织大量的协程,协程之间如何进行通信,协程怎么优雅退出,如何保证并发安全,这些是我们在设计高并发的模型时需要考虑的问题。好的并发模型能够提升程序的性能、扩展性与安全性。
这节课,我们就来看看一些富有表现力的高并发模型。让我们先从并发带来的问题说起。

数据争用

在 Go 语言中,当两个以上协程同时访问相同的内存空间,并且至少有一个写操作时,就可能会出现并发安全问题,这种现象也被叫做数据争用。在下面这个例子中,两个协程共同访问了全局变量 count。这个程序其实是有数据争用的,因为 count 的最终结果是不明确的。
var count = 0
func add() {
count++
}
func main() {
go add()
go add()
}
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
立即购买
登录 后留言

全部留言(4)

  • 最新
  • 精选
  • Geek_c16d38
    func 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归属地:江苏
  • shuff1e
    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)} go c改成go worker?
    2022-12-08归属地:北京
    1
收起评论
显示
设置
留言
4
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部