逻辑时钟算法

文章介绍了Lamport时钟和向量时钟的概念,用于在分布式系统中确定事件的相对顺序。Lamport时钟保证了单个进程内事件的顺序,而向量时钟则能处理跨进程的事件顺序,通过比较向量时间戳来推断事件的并发或顺序关系。在向量时钟中,若一个向量时间戳的所有元素都小于另一个,可以确定一个事件发生在另一个之前。
摘要由CSDN通过智能技术生成

Lamport 时钟

定义a->b为事件a发生在事件b之前,进程为Pi,进程对应本地逻辑时钟为Ci,Ci(a)代表进程P发生事件a时的逻辑时钟值,则Ci的更新如下

  1. 进程Pi每发生事件,Ci加1
  2. 进程Pi每发送进程Pj消息,Pj根据Pi传递过来的Ci与自己的Cj比较取更大值加1

那么显然对于a->b,C(a)<C(b)。a、b是顺序发生的,那么在事件b所在进程接受之后计算得到的时钟一定大于事件a所在进程的时钟

但C(a)<C(b)不一定a->b,因为a、b可能是并行发生的。a所在的进程并没有在a完成之后顺序执行调用事件b

type LamportsProcess struct {
	mu    *sync.Mutex
	id    int
	clock int
}

// 产生事件时,时间戳加1
func (l *LamportsProcess) event() {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.clock++
}

// 发送消息时,时间戳加1
// 接受者的时间戳取自己的时间戳和接受者的时间戳的最大值加1
func (l *LamportsProcess) send(p *LamportsProcess) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.clock++
	p.receive(l)
}

// 接受消息时,时间戳取自己的时间戳和发送者的时间戳的最大值加1
func (l *LamportsProcess) receive(p *LamportsProcess) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.clock = maxInt(l.clock, p.clock) + 1
}

func maxInt(x, y int) int {
	if x > y {
		return x
	}
	return y
}

向量时钟

向量时钟不仅同步本进程的时钟值,而且还同步已知的其他进程时钟值

以此去实现在Ci>Cj情况下,能推导出a->b

分布式系统中每个进程Pi保存一个本地逻辑时钟向量值VC(i),VCi(j)代表进程Pi知道的进程Pj的本地逻辑时钟值

  1. 初始化VCi向量为[0,…]
  2. 进程Pi每发生一次事件,VCi[i]加一
  3. 进程Pi给进程Pj发送消息,需带上自己的向量时钟VCi
  4. 进程Pj接收消息后
    1. 更新VCj向量为VCj向量与VCi向量中的最大值
    2. 将VCj中自己对应时钟值加一,也就是VCj[j]加一

向量时钟里向量时间戳是一种偏序关系。

如果向量时间戳VC(i)中所有时间戳都大于对应向量时间戳VC(j),那么VC(i)>VC(j)

如果向量时间戳VCi中所有时间戳都等于对应向量时间戳VC(j),那么VC(i)=VC(j)

否则则不能比较,两向量为并发

从以上算法可以很容易地得出下面两个结论:

  1. 同一个进程内的两个事件a和b,如果 a → b,那么 VCi (a) < VCi (b)。
  2. a是Pi进程的消息发送事件,b是Pj进程该消息的接收事件,那么 VCi (a) < VCj (b)。
type VectorProcess struct {
	mu *sync.Mutex
	// 进程id
	id int
	// 向量时间戳
	vClock []int
}

func NewVectorProcess(id, totalProc int) *VectorProcess {
	vClock := make([]int, totalProc)
	return &VectorProcess{
		mu:     &sync.Mutex{},
		id:     id,
		vClock: vClock,
	}
}

// 产生事件时,时间戳加1
func (p *VectorProcess) event() {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.vClock[p.id]++
}

// 发送消息时,时间戳加1
func (p *VectorProcess) send(q *VectorProcess) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.vClock[p.id]++
	q.receive(p)
}

// 接受消息时,时间戳取自己的时间戳和发送者的时间戳的最大值加1
func (p *VectorProcess) receive(q *VectorProcess) {
	p.mu.Lock()
	q.mu.Lock()
	defer p.mu.Unlock()
	defer q.mu.Unlock()

	for i := 0; i < len(p.vClock); i++ {
		p.vClock[i] = maxInt(p.vClock[i], q.vClock[i])
	}
	p.vClock[p.id]++
}

证明

那么如何证明VC(i) < VC(j) 可以推导a → b呢?

如果事件a、b在同一个进程,那么肯定a->b

如果事件a、b分别在进程Pi和Pj中,那么设c为发送a更新事件,d为接收a更新事件。设VC(i)=[m, n],VC(j)=[s, t]

在VC(i)<VC(j)情况下,m<=s,所以在事件a发生之后,事件b发生之前Pi一定向Pj发送了消息,那么接受a更新的事件d一定不晚于b,因此

在这里插入图片描述

  • 如果a=c且d=b,那么显然a->b
  • 如果a=c且d->b,那么a->b
  • 如果a->c且d=b,那么a->b
  • 如果a>c且d->b,那么a->b

综上所述,可得VC(i)<VC(j)的情况下,a->b

Ref

  1. https://yang.observer/2020/09/12/vector-clock/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值