GO语言多线程实践-2

实践背景

本次实践的背景依然MIT的6.824课程,在课程中lab2需要实现Raft算法,Raft算法的原理细节在我的另外一篇博客中,在这里就不再赘述了。

Raft中Leader Election

在Raft算法中,当一个节点变为candidate节点之后,其他的节点需要对其进行投票,当其获得超过半数票数时即当选,反之落选。下面将对该过程进行简单的模拟实践。

实践思路

1.首先向其他节点发送投票请求;
2.根据投票请求的结果进行票数计算;
3.根据票数的结果,得出投票的结果。

那么在第一个步骤中,一定是并发的同时向所有节点发送投票请求,而非串行的发送请求。所以这里需要用到多线程协同。其次在票数统计时,会涉及到用几个变量来记录票数的情况,在多线程情况下操作这些变量则需要lock来保证线程安全。

具体代码

根据以上思想,完成简易的实现代码如下:

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

func main() {
	count := 0
	finshed := 0
	var mu sync.Mutex
	rand.Seed(time.Now().UnixNano())

	//假设一共10个节点
	for i := 0; i< 10 ; i++ {
		go func() {
			vote := requestVote()
			mu.Lock()
			defer mu.Unlock()
			if vote {
				count++
			}
			finshed++
		}()
	}
    for {
		mu.Lock()
		if count >5 || finshed == 10 {
			break
		}
		mu.Unlock()
	}
	if count > 5 {
		fmt.Println("received major voted!")
	}else {
		fmt.Println("failed!")
	}
}

func requestVote() bool {
	//随机模拟投票时间和投票结果
	time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
	return rand.Int() % 2 == 0
}

这里利用rand随机包来实现请求投票requestVote的模拟,首先等待一个随机事件,然后再返回一个随机的bool类型。值得注意的是,需要对count以及finshed这两个变量的操作都加锁,保证线程安全。

发现问题

上述代码看似没有问题,实则存在一个性能隐患。本身我们在完成这段代码时,并未考虑性能问题,只是完成具体的功能即可。但是,请看下面这段代码:

for {
		mu.Lock()
		if count >5 || finshed == 10 {
			break
		}
		mu.Unlock()
	}

这里的死循环,可能会导致cpu的某个核的利用率达到了100%,为避免这种情况,可以让其休眠一定时间再重新拉起,使得cpu资源可以被释放,如下:

for {
		mu.Lock()
		if count >5 || finshed == 10 {
			break
		}
		mu.Unlock()
		time.sleep(5 * time.Seconed)
	}

继续优化

上述问题虽然解决了,但是依然不够优化,在多个线程需要根据共享变量的结果来执行某些条件操作时,可以用到条件变量,如下:

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

func main() {
	count := 0
	finshed := 0
	var mu sync.Mutex
	cond := sync.NewCond(&mu) //设置条件变量,提高程序性能
	rand.Seed(time.Now().UnixNano())

	//假设一共10个节点
	for i := 0; i< 10 ; i++ {
		go func() {
			vote := requestVote()
			mu.Lock()
			defer mu.Unlock()
			if vote {
				count++
			}
			finshed++
			cond.Broadcast()//唤醒所有在wait的线程
		}()
	}
	mu.Lock()
	for count < 5 && finshed != 10 {
		cond.Wait()//线程挂起等待
	}
	if count > 5 {
		fmt.Println("received major voted!")
	}else {
		fmt.Println("failed!")
	}
	mu.Unlock()
}

func requestVote() bool {
	//随机模拟投票时间和投票结果
	time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
	return rand.Int() % 2 == 0
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值