MIT 6.824 分布式系统课程第五课:Go线程和Raft

Posted by 启示录 Blog on April 3, 2020

笔记:Lecture 5: Go, Threads, and Raft

视频:Lecture 5: Go, Threads, and Raft

论文地址:The design of a practical system for fault-tolerant virtual machines

课程概要

Go语言内存模型、并发原语、并发模式、调试

正文内容

Go内存模型这篇文章讲述go语言中的内存模型,它告诉我们如何写出正确的多线程代码。goroutines和并发在go中有着千丝万缕的关系,性能、便捷性。经过语言的不断迭代,goroutines的性能也得到极大的提升。本课的重点在于如何写正确的并发代码,而不是性能。

“write correct code”.

Goroutines,闭包

goroutines是轻量级线程,彼此并发运行。当main goroutine退出时,程序自动终止,与此同时程序运行的其他goroutines也会终止运行。

闭包:标示符捕获,gotchas绑定

  • closure.go:go中拥有头等函数,配合goroutines使用。对变量的作用域进行限制,让声明的函数只作用域闭包内部。
  • loop.go:循环创建goroutine(输出结果不一定按顺序输出,因为并发,并非同步执行)
  • bad.go:变量i访问的是gorountine外部变量,每个goroutine访问时的结果都不确定,受外部循环执行快慢的影响。

Time

  • sleep.go:time.Now()、time.Sleep()
  • sleep-cancel.go:不要写无限循环的代码。使用锁时要小心死锁。函数返回(return)前需要释放锁

Mutexes:互斥量

  • bad.go
  • basic.go:基本使用defer
  • per-field.go:锁保护是保护变量,不是保护访问共享数据。锁的目的是为了在访问或者是修改变量时,只有单个线程在操作。其结果不受其他线程的影响。所以转账的增和减应在同一个锁里面。

    关键:临时暂停其他线程的修改、unlock解锁访问。没有其他线程打断访问

    锁:确保lock()与unlock之间的代码执行的原子性,防止竞争访问或者修改

  • bank.go:银行转账问题

条件变量

  • 等待(wait)、信号(signal)、广播(broadcast)
  • vote-count-1.go ….vote-count-4.go
  • cond.txt,如何避免竞争条件。
    • 锁的使用
    • 使用循环检查条件状态,也可以使用条件变量sync.Cond
    • 锁应避免在会导致CPU100%的代码段中使用

Channel:通道

  • 类似同步队列原语
  • producor-consumer.go 生产消费者模型
  • wait等待前置调用完成
  • unbuffered.go:未初始化长度的通道,同步
  • deadlock.go:死锁

Debugging:调试

  • 在Raft目录下可以使用util.go中的DPrintf调试,输出日志
  • 使用-race帮助识别数据竞争
    • go test -race -run 2A
    • not a proof
    • race不是万能的,不是所有的竞争数据情况都能识别到
  • SIGQUIT
    • SIGQUIT默认监听所有的goroutines.并打印栈
    • Ctrl+\可以发送SIGQUIT到当前进程
  • 处理goroutines溢出
    • 使用ps命令查看运行的进程
    • 发送SIGQUIT或者是SIGKILL信号给进程:kill -QUIT pid 或 kill -KILL pid
  • 并行

常用工具

  • 使用man命令查看工具使用的手册
  • 或者使用tldr命令快速的查看手册:tldr.sh
  • 重定向输出
    • stdout输出正常结果
    • stderr输出错误结果
    • >&>
      • 重定向输出结果到文件,错误会接着显示
      • & 错误和标准输出都写入到文件
    • tee、通道||&
      • tee重定向输出到文件并显示
      • echo "example" | tee FILE
      • 使用|&可以重定向标准输出和错误
  • 文件阅读
    • head、tail、less
    • grep
      • -i不区分大小写
      • -n显示行号
      • grep -in searchString file
    • ripgrep Ripgrep
      • 和grep类似,但支持整个目录

参考资料