這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
前段在玩協程時跟著libtask的作者Russ Cox轉到Golang了,一堆重量級大牛們想出來的東西就是不同,思路非常特別,和常用的差別有點大,
但非常注重實用,減少了一堆的文法糖後,讓人重新思考,開發語言本來應當是怎麼樣?如果用來做服務端開發的話,真的是很不錯的東西。
在一個練習中,我設計了個發送OracleDatabase Backup任務的簡單情境。通過 發送備份任務到各個用戶端,來練習Golang中的sync.WaitGroup.
sync.WaitGroup是Golang提供的一種簡單的同步方法集合。它有三個方法.
Add() 添加計數,數目可以為一個,也可以為多個。
Done() 減掉一個計數,如果計數不為0,則Wait()會阻塞在那,直到全部為0
Wait() 等待計數為0.
弄個例子直觀的見識下:
package mainimport ("fmt""sync")var waitGrp sync.WaitGroupfunc main() {bakJobs := []string{"Full Database", "Archive Log", "Control File"}for i := 0; i < len(bakJobs); i++ {waitGrp.Add(1) //add jobgo func(jobName string) {fmt.Println(jobName)waitGrp.Done() //sub job}(bakJobs[i])}waitGrp.Wait()}/*E:\GitHub\Golang\XCLTools\src\test>wgFull DatabaseArchive LogControl File*/
可以看到會依次列印bakJobs中的資料。但如果把waitGrp.Wait()注釋掉。啥都不會輸出。即,在for後,wait會等待所有的job執行完後,
才會執行後面的。 這個例子說明,除了熟悉了下waitGroup的使用外,還說明waitGroup能保證作業的執行順序,及會等待所有子作業全部完成再繼續的特性。
就OracleDatabase Backup本身而言,在這使用go func()...其實意義不大,所以我想了個更複雜點的情境,給多個伺服器上的資料庫批量發送備份指令,並依資料庫
的重要性順序來執行。
下面是實現的代碼(純示範,忽略掉其它通訊和邏輯處理方面的東西):
package mainimport ("fmt""sync")var waitGrp sync.WaitGroupfunc main() {waitGrp.Add(2)for _, ip := range []string{"ip1", "ip2"} {go doJobs(ip)fmt.Println("IP = ", ip)}waitGrp.Wait()fmt.Println("main() end.")}func doJobs(ip string) {bakJobs := []string{"Full Database", "Archive Log", "Control File"}for i := 0; i < len(bakJobs); i++ {doBak(ip, i, bakJobs[i])}fmt.Println("The backup is complete!")fmt.Println("......")defer waitGrp.Done()}func doBak(ip string, id int, jobName string) (bool, error) {fmt.Println("doBak() :", ip, " - ", id, " - ", jobName)return true, nil}/*IP = ip1IP = ip2doBak() : ip1 - 0 - Full DatabasedoBak() : ip1 - 1 - Archive LogdoBak() : ip1 - 2 - Control FileThe backup is complete!......doBak() : ip2 - 0 - Full DatabasedoBak() : ip2 - 1 - Archive LogdoBak() : ip2 - 2 - Control FileThe backup is complete!......main() end.*/
代碼中,會首先向各個IP發送執行jobs的指令。各個指令執行的時間有長有短,用go func()...能保證是在同一時間向所有相關IP發出命令讓各IP依次執行相關備份。
主goroutine則等待這個總作業全部結束後再執行其它。同時由於waitGroup的順序性,能保證重要點的ip,可以優先執行。
我設計情境的目的是想在例子中加入更多的Golang新鮮元素來增強感覺,所以針對上一個例子,我又設想了一個情況,因為Database Backup是很耗時的,且要等待
所有IP上的作業都執行完畢要花費很長時間,如果這時,使用者要求取消這次作業怎麼辦?
於是這就有了下面的例子:
package mainimport ("bufio""fmt""os""sync""time")var waitGrp sync.WaitGroupfunc main() {ch := make(chan bool)go schedule(ch)r := bufio.NewReader(os.Stdin)for {time.Sleep(time.Second)fmt.Print("Command:> ")ln, _, _ := r.ReadLine()cmd := string(ln)if "q" == cmd || "quit" == cmd {close(ch)break} else {fmt.Println(" = cmd = ", cmd, "\n")}}waitGrp.Wait()fmt.Println("main() end.")}func schedule(ch chan bool) {for _, ip := range []string{"ip1", "ip2"} {waitGrp.Add(1)go doJobs(ip, ch)fmt.Println("schedule() IP = ", ip)}fmt.Println("schedule() end.")return}func doJobs(ip string, ch chan bool) {defer waitGrp.Done()for i := 0; i < 10; i++ {select {case <-ch:fmt.Println("doJobs() ", ip, "=>Job Cancel......")returndefault:}fmt.Println("doJobs()...... ", ip, " for:", i)time.Sleep(time.Second)}}/*E:\GitHub\Golang\XCLTools\src\test>wg5schedule() IP = ip1schedule() IP = ip2schedule() end.doJobs()...... ip1 for: 0doJobs()...... ip2 for: 0Command:> doJobs()...... ip1 for: 1doJobs()...... ip2 for: 1doJobs()...... ip1 for: 2doJobs()...... ip2 for: 2doJobs()...... ip1 for: 3doJobs()...... ip2 for: 3doJobs()...... ip1 for: 4doJobs()...... ip2 for: 4doJobs()...... ip1 for: 5doJobs()...... ip2 for: 5doJobs()...... ip1 for: 6doJobs()...... ip2 for: 6doJobs()...... ip1 for: 7doJobs()...... ip2 for: 7doJobs()...... ip1 for: 8doJobs()...... ip2 for: 8doJobs()...... ip1 for: 9doJobs()...... ip2 for: 9qmain() end.E:\GitHub\Golang\XCLTools\src\test>wg5schedule() IP = ip1schedule() IP = ip2schedule() end.doJobs()...... ip1 for: 0doJobs()...... ip2 for: 0Command:> doJobs()...... ip2 for: 1doJobs()...... ip1 for: 1doJobs()...... ip2 for: 2doJobs()...... ip1 for: 2doJobs()...... ip2 for: 3doJobs()...... ip1 for: 3qdoJobs() ip2 =>Job Cancel......doJobs() ip1 =>Job Cancel......main() end.*/
我用了個for{},讓使用者在作業執行過程中,可以輸入指令退出執行。並且,在退出過程中,各個IP也會作相關的取消處理。以保證不會因
強制中斷而出現一些不必要的麻煩。
這個例子到這,已經引入了足夠多的東西,我覺得已達到練習的目的,情境就先到此了。
在此整理下,以備後查。
MAIL: xcl_168@aliyun.com
BLOG: http://blog.csdn.net/xcl168