為什么要寫(xiě)這篇博客
最近在學(xué)習(xí)Go并發(fā)的時(shí)候筏餐,想寫(xiě)一個(gè)并發(fā)的求斐波那契數(shù)的程序意鲸。期間遇到了一些坑抓于,所以來(lái)記錄一下自己的想法。
寫(xiě)并發(fā)程序中遇到的坑
并發(fā)1.0
使用最原始的方式羊异,每次遞歸的時(shí)候事秀,開(kāi)一個(gè)協(xié)程去跑彤断,將結(jié)果放入channel中。最終發(fā)現(xiàn)在求的數(shù)比較大的時(shí)候易迹,并發(fā)比單線程還要慢宰衙。
并發(fā)2.0
于是我考慮應(yīng)該是開(kāi)的協(xié)程太多了,使用協(xié)程池來(lái)控制協(xié)程數(shù)量睹欲。代碼如下:
func mutiExample(pool *tunny.Pool, ch chan int64, n int64) {
var ans int64
switch n {
case 0:
ans = 0
case 1, 2:
ans = 1
default:
res := make(chan int64, 2)
a := func() {
fmt.Println("start ", n-1)
mutiExample(pool, res, n-1)
}
b := func() {
fmt.Println("end ", n-2)
mutiExample(pool, res, n-2)
}
go pool.Process(a)
go pool.Process(b)
ans = <-res + <-res
}
ch <- ans
}
func main() {
start := time.Now()
pool2 := tunny.NewCallback(3)
ch := make(chan int64, 1)
mutiExample(pool2, ch, 5)
fmt.Println(<-ch, time.Since(start))
}
發(fā)現(xiàn)的問(wèn)題
現(xiàn)象
- 如果求的值為5供炼,協(xié)程數(shù)量為3,一定概率會(huì)死鎖窘疮;
- 如果求的值為5袋哼,協(xié)程數(shù)量為4,則不會(huì)死鎖闸衫;
分析
我們來(lái)通過(guò)一組結(jié)果分析它的執(zhí)行過(guò)程
start 4
end 3
end 1
end 2
start 3
- 顯然涛贯,程序先開(kāi)了兩個(gè)協(xié)程去跑 n=4 和 n=3 的情況,由于未獲取全部返回蔚出,協(xié)程會(huì)一直阻塞;
- 然后是 n=2 或 n=1 的情況弟翘,將結(jié)果寫(xiě)入channel,并結(jié)束身冬;
-
最后執(zhí)行 n=3 的情況衅胀,由于3個(gè)協(xié)程全部被占用岔乔,且它們所期望的值無(wú)法返回酥筝,會(huì)造成死鎖。
流程.png
結(jié)論
如果要讓程序不出現(xiàn)死鎖雏门,則需要限定協(xié)程池大小大于執(zhí)行 n>2 的協(xié)程的總數(shù)量嘿歌。
因?yàn)檫@種情況下程序無(wú)法直接返回,在等待接收數(shù)據(jù)茁影。
自己的一些思考
- 在當(dāng)時(shí)遇到這個(gè)問(wèn)題的時(shí)候宙帝,自己一直在想是不是自己程序出了問(wèn)題,而沒(méi)有對(duì)程序的執(zhí)行流程進(jìn)行分析募闲。所以也是給自己提了個(gè)醒步脓,在寫(xiě)程序的時(shí)候,要提前能想清楚程序的執(zhí)行流程浩螺,不要盲目的依賴程序的運(yùn)行結(jié)果靴患。
- 你會(huì)發(fā)現(xiàn)2.0程序的執(zhí)行效率還是很低,可以發(fā)現(xiàn)這種遞歸程序要出,盲目的去開(kāi)協(xié)程鸳君,是不會(huì)對(duì)效率有提升的。因?yàn)楫?dāng)遞歸層數(shù)很多時(shí)患蹂,協(xié)程池在調(diào)度協(xié)程等問(wèn)題上會(huì)花費(fèi)很多的時(shí)間或颊。
- 貼出代碼的原因是因?yàn)樽约旱木幋a規(guī)范實(shí)在太差砸紊,比如:命名,取channel的值等囱挑。
總結(jié)
- 寫(xiě)并發(fā)程序時(shí)醉顽,首先要選取好合適的并發(fā)方案,不能為了并發(fā)而并發(fā)平挑;
- 并發(fā)程序不能太相信結(jié)果(協(xié)程執(zhí)行順序不確定)徽鼎,而是要想清楚代碼執(zhí)行的流程(道理)
- 要加強(qiáng)自己對(duì)代碼規(guī)范的要求
- 要善用測(cè)試,這個(gè)以后再補(bǔ)充吧弹惦。否淤。