MIT6.824 Lecture5 Go Thread and Raft

· wordCount · 2 minute read

Lecture5 Go Thread and Raft

讲义:http://nil.csail.mit.edu/6.824/2020/notes/l-go-concurrency.txt

视频:https://www.bilibili.com/video/BV1qk4y197bB?p=5

前半部分主要介绍Go并发相关知识,后半部分介绍raft lab的实现,只针对前半部分的一些内容做了摘要。

Go相关

closure&wait group

good

func main(){
  var wg sync.WaitGroup
  for i:=0; i< 5; i+ {
		wg.Add(1)
		go func(x int) {
			sendRPC(x)
			wg.Done()
		}
  }
	wg.Wait()
}

func sendRPC(i int){
	println(i)
}

bad

func main() {
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			sendRPC(i)
			wg.Done()
		}()
	}
	wg.Wait()
}

func sendRPC(i int) {
	println(i)
}

Mutex

加锁,确保在关键代码区互斥

bad

import "time"

func main() {
	counter := 0
	for i := 0; i < 1000; i++ {
		go func() {
			counter = counter + 1
		}()
	}

	time.Sleep(1 * time.Second)
	println(counter)
}

good

import "time"

func main() {
	counter := 0
	var mu sync.Mutex
	for i := 0; i < 1000; i++ {
		go func() {
			mu.Lock()
			defer mu.Unlock()
			counter = counter + 1
		}()
	}

	time.Sleep(1 * time.Second)
	mu.Lock()
	println(counter)
	mu.Unlock()
}

条件变量

  • func NewCond(l Locker) *Cond

    使⽤锁 I 创建一个 *Cond。 Cond条件变量,总是要和锁结合使用。

  • func (c *Cond) Broadcast()

    Broadcast唤醒所有等待c的线程。调⽤者在调⽤本⽅法时,建议(但并⾮必须)保持c.L的锁定。

  • func (c *Cond) Signal()

    Signal唤醒等待c的⼀个线程(如果存在)。调⽤者在调⽤本⽅法时,建议(但并⾮必须)保持c.L的锁定。 发送通知给⼀个⼈。

  • func (c *Cond) Wait()

    a) Wait⾃⾏解锁c.L并阻塞当前线程,在之后线程恢复执⾏时, Wait⽅法会在返回前锁定c.L。和其他系统不同, Wait除⾮被Broadcast或者Signal唤醒,不会主动返回。 ⼴播给所有⼈。

    b) 因为线程中Wait⽅法是第⼀个恢复执⾏的,⽽此时c.L未加锁。调⽤者不应假设Wait恢复时条件已满⾜,相反,调⽤者应在循环中等待。

import "sync"
import "time"
import "math/rand"

func main() {
	rand.Seed(time.Now().UnixNano())

	count := 0
	finished := 0
	var mu sync.Mutex
	cond := sync.NewCond(&mu)

	for i := 0; i < 10; i++ {
		go func() {
			vote := requestVote()
			mu.Lock()
			defer mu.Unlock()
			if vote {
				count++
			}
			finished++
			cond.Broadcast()
		}()
	}

	mu.Lock()
	for count < 5 && finished != 10 {
		cond.Wait()
	}
	if count >= 5 {
		println("received 5+ votes!")
	} else {
		println("lost")
	}
	mu.Unlock()
}

pattern

mu.Lock()
// do something that might affect the condition
cond.Broadcast() // or.signal() wake up one thread
mu.Unlock()

----

mu.Lock()
while condition == false {
	cond.Wait()
}
// now condition is true, and we have the lock
mu.Unlock()

Broadcast和signal的效果其实差不多,broadcast唤醒所有等待者,signal只唤醒一个。signal也许可以提高一些性能。

channel

可以用作wait group

package main

func main() {
	done := make(chan bool)
	for i := 0; i < 5; i++ {
		go func(x int) {
			sendRPC(x)
			done <- true
		}(i)
	}
	for i := 0; i < 5; i++ {
		<-done
	}
}

func sendRPC(i int) {
	println(i)
}

代码中应该避免使用带buffer的chan,除非有非常清晰的目的。