MIT 6.824 Lecture 2 RPC and Threads Notes
Posted 周某的技术笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MIT 6.824 Lecture 2 RPC and Threads Notes相关的知识,希望对你有一定的参考价值。
MIT 6.824 Lecture 2 RPC and Threads Notes
1 概要
本课没有涉及分布式系统方面的内容,主要是针对本课程 Lab 使用的编程语言 Go 进行了一个简单的介绍,然后讨论了一下多线程并发相关内容。最后是对一个 Go 写的多线程爬虫代码进行了解读,关注点在并发处理、竞态、锁、多线程协作这块。
本课相关的材料:
•课堂 Paper: 本课无论文阅读•课堂录像: https://www.bilibili.com/video/BV1R7411t71W?p=2•课堂 Note: https://pdos.csail.mit.edu/6.824/notes/l-rpc.txt
2 要点
2.1 Why Go
•Thread (goroutine) 支持•Lock: 锁机制应对并发执行和竞态•类型安全•方便的 RPC 库•GC 内存安全: 垃圾回收
Go 要比 C++ 更容易使用,语言更简单直接,不会有特别的语法和特性,也不会有那么多奇怪的错误。
2.2 关注并发
•I/O concurrency: 客户端可以请求多个服务端并发等待响应,服务端处理多个客户端的连接请求,在一个请求进行 IO 操作时可以切换处理另外一个请求的计算;•Parallelism: 多线程利用多核,实际系统中应该尽量利用所有 CPU 的计算力;•Convenience: 后台运行,方便执行处理一些分离的任务;
不用多线程,可以用异步编程 event-driven 的方式:
•单线程单 loop;•保存每个状态: 比如请求客户端的状态;•根据事件来执行切换执行任务;•单个运行无法充分利用多核 CPU;•实现相对复杂,使用起来也难以理解
相对大量线程的情况会更优秀,比如有上百万的连接,对应上百万个线程来说,事件驱动更好,节省资源,同时还可以减少线程切换带来的性能损耗。实现上通常可以多个线程,每个线程都有个独立的事件循环来执行任务,这样可以利用多核资源。比如 nginx,是基于多 Worker 线程的事件驱动模型来实现高性能并发处理大量请求的支持。
2.3 多线程的挑战
共享数据、竞态数据: 多线程访问处理容易出现 bug,并发更新可能会出现问题,机器操作可能不是原子指令
•需要使用锁来解决这个问题;•或者避免共享可变数据;
coordination 多线程协作执行
•channels•sync.Cond•waitGroup
死锁
•锁或者 channel 误用,出现彼此依赖释放或者消费的情况,导致了死锁;
2.4 爬虫示例
示例代码主要是实现模拟爬虫处理页面抓取的功能,需要考虑以下内容:
•一个页面可能还包含了其他的页面 URL•多个页面可能包含同一个 URL,不应该重复抓取•多个页面直接包含 URL 可能会构成一个环•页面抓取应当并发进行,可以加速整个任务的执行
课堂上主要是介绍了两个版本的并发抓取爬虫:
1 基于锁的并发爬虫
•每个发现的 URL 都创建一个抓取页面的线程•多个线程之间共享一个 map 数据来记录已经抓取到的页面,避免重复和循环抓取•多个线程对共享的 map 数据操作时需要加锁,避免出现竞态并发更新/读取,在 Go 这会导致 panic 或者内部数据异常•可以通过 go 编译器自身的 -race
工具来检查代码中的竞态问题
2 基于 Channel 的并发爬虫
•区分为 Master 和 Worker 线程•Master 线程创建 Worker 线程去抓取单个页面•Master 和 Worker 线程之间共享一个 channel,Worker 把抓取到的页面里面包含的 URL 发送到这个 channel;•Master 记录 Worker 执行抓取过的 URL,从 channel 获取到新的页面,先检查是否已经抓取过,如果没有则启动新的 Worker 线程抓取,有则跳过;
基于 channel 不需要加锁,是因为记录抓取过页面的 map 数据实际上没有在多个线程中共享,也不存在多线程并发读取更新的情况。但是实际上,channel 数据结构本身在 Go 的实现应该是存在着锁的,这样多个线程每次只有一个线程可以把 URL 发送到 channel 中。
3 总结
本课内容相对简单,Go 语言对于并发的支持比较好,提供了方便的线程(goroutine) 启动方式,此外还对多线程间的协作提供了包括 channel 、sync 等工具来支持。课程原本是用 C++ 来实现 Lab 相关的编码的,近些年在 Go 语言成熟起来后就切换了。使用 Go 来学习和实现分布式系统,可以让学生更关注分布式系统本身相关的内容,而不是在 C++ 的语言特性和代码 Bug 中花费大量的时间。
Go 语言本身也比较适合网络编程,在业界中有不少的成熟的分布式系统实现,比如 etcd、TiDB、Kubernetes 等。
以上是关于MIT 6.824 Lecture 2 RPC and Threads Notes的主要内容,如果未能解决你的问题,请参考以下文章
MIT 6.824学习笔记 2: RPC and Threads