您好,登錄后才能下訂單哦!
這篇文章主要介紹“Golang協(xié)程常見面試題代碼分析”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強(qiáng),希望這篇“Golang協(xié)程常見面試題代碼分析”文章能幫助大家解決問題。
使用兩個goroutine交替打印1-100之間的奇數(shù)和偶數(shù), 輸出時按照從小到大輸出.
package main import ( "fmt" "sync" ) // PrintOddAndEven1 /* func PrintOddAndEven1() { //方法一,使用無緩沖的channel進(jìn)行通信 var wg = new(sync.WaitGroup) //注意這里需要是指針go語言當(dāng)中都是值傳遞 wg.Add(2) ch := make(chan struct{}) //無緩沖channel defer close(ch) maxVal := 100 go func() { defer wg.Done() for i := 1; i <= maxVal; i++ { ch <- struct{}{} if i%2 == 1 { //奇數(shù) fmt.Printf("the odd is %d\n", i) } } }() go func() { defer wg.Done() for i := 1; i <= maxVal; i++ { <-ch //從管道當(dāng)中讀取一個數(shù)據(jù) if i%2 == 0 { //偶數(shù) fmt.Printf("the even is %d\n", i) } } }() wg.Wait() } func main() { PrintOddAndEven1() fmt.Println("over") }
下面博主來解釋一下這個的原理 首先因為變量ch是一個無緩沖的channel, 所以只有讀寫同時就緒時才不會阻塞。所以兩個goroutine會同時進(jìn)入各自的 if 語句(此時 i 是相同的),但是此時只能有一個 if 是成立的,不管goroutine快,都會由于讀channel或?qū)慶hannel導(dǎo)致阻塞,因此程序會交替打印1-100且有順序。
func PrintOddAndEven2() { var wg = new(sync.WaitGroup) //注意這里需要是指針go語言當(dāng)中都是值傳遞 wg.Add(2) oddChan := make(chan struct{}, 1) eveChan := make(chan struct{}, 1) defer close(oddChan) defer close(eveChan) oddChan <- struct{}{} maxVal := 20 go func() { //奇數(shù)協(xié)程 defer wg.Done() for i := 1; i <= maxVal; i += 2 { <-oddChan fmt.Printf("the odd print %d\n", i) eveChan <- struct{}{} //通知偶數(shù)協(xié)程 } }() go func() { //偶數(shù)協(xié)程 defer wg.Done() for i := 2; i <= maxVal; i += 2 { <-eveChan fmt.Printf("the even print %d\n", i) oddChan <- struct{}{} //通知奇數(shù)協(xié)程可以打印了 } }() wg.Wait() } func main() { PrintOddAndEven2() fmt.Println("over") }
第二個方法使用這個有緩沖的channel。有緩沖的channel當(dāng)容量沒有達(dá)到上限時寫入不會阻塞在這里奇數(shù)協(xié)程的channel容量為1我們提前給他寫入了一個數(shù)據(jù)因此當(dāng)偶數(shù)和奇數(shù)協(xié)程都開始讀取數(shù)據(jù)時,首先讀取到數(shù)據(jù)的是奇數(shù)協(xié)程,奇數(shù)協(xié)程打印完之后在通知偶數(shù)協(xié)程打印,偶數(shù)協(xié)程打印完成之后在通知奇數(shù)協(xié)程重復(fù)下去就實現(xiàn)了交替打印的效果。
題目描述非常的簡單就是N個協(xié)程交替打印1到maxVal。比如N=3,maxVal是這個100效果應(yīng)該是第一個協(xié)程打印1,第二個協(xié)程打印2,第三個協(xié)程打印3,第一個協(xié)程打印4這樣的效果。
這道題看起來非常的復(fù)雜,博主第一次看到這個題的時候也感覺很復(fù)雜但是仔細(xì)想一下其實并沒有那么復(fù)雜和上面兩題的解題思路是一樣的。下面我們看看這個代碼如何實現(xiàn)
package main import ( "fmt" "sync" ) func main() { maxVal := 10 res := 0 //用于打印數(shù)字 N := 3 //協(xié)程的數(shù)量 exitChan := make(chan struct{}) //用于退出 chanArr := make([]chan struct{}, N) for i := 0; i < N; i++ { //使用無緩沖的channel chanArr[i] = make(chan struct{}, 1) } num := 0 //記錄輪到那個協(xié)程開始打印了 chanArr[0] <- struct{}{} for i := 0; i < N; i++ { go func(i int) { for { <-chanArr[i] if res >= maxVal { exitChan <- struct{}{} break } fmt.Printf("第%d個協(xié)程打印%d\n", i, res) if num == N-1 {//已經(jīng)循環(huán)一輪了輪到第0個協(xié)程打印數(shù)據(jù)了 num = 0 } else { num++ } res++ chanArr[num] <- struct{}{} //第num個協(xié)程可以打印數(shù)據(jù)了 } }(i) } <-exitChan for i := 0; i < N; i++ { close(chanArr[i]) //將管道全部關(guān)閉否則會有協(xié)程泄漏 } }
其實也非常的簡單也是利用channel來進(jìn)行這個協(xié)程之間的通信,由于是N個協(xié)程之間進(jìn)行通信所以了我們定義一個channel的切片首先往第一個channel當(dāng)中寫入一個數(shù)據(jù)其他管道沒有寫入數(shù)據(jù)那么最先打印的一定是這個第一個協(xié)程然后我們在利用一個計數(shù)器通知其他協(xié)程打印。最后需要注意的是主協(xié)程退出時需要將管道全部關(guān)閉否則其他協(xié)程一致阻塞在那里就會引起協(xié)程泄漏,就只能等到gc的時候才能回收。
問題描述: 使用兩個 goroutine 交替打印序列,一個 goroutinue 打印數(shù)字, 另外一個goroutine打印字母, 最終效果如下 12AB34CD56EF78GH910IJ 。
如果鐵子們上面兩題會了那么這道題就是有手就行的那種和第一道題沒有啥區(qū)別
func main() { numChan := make(chan struct{}, 1) chChan := make(chan struct{}, 1) defer close(numChan) defer close(chChan) var wg sync.WaitGroup wg.Add(2) numChan <- struct{}{} go func() { defer wg.Done() for num := 1; num <= 26; num++ { <-numChan fmt.Printf("%d", num) chChan <- struct{}{} } }() go func() { defer wg.Done() for ch := 'A'; ch <= 'Z'; ch++ { <-chChan fmt.Printf("%s", string(ch)) numChan <- struct{}{} } }() wg.Wait() }
同樣的也是利用這個channe進(jìn)行通信,利用有緩沖的channel進(jìn)行通信。當(dāng)然也能使用這個無緩沖的channel進(jìn)行通信
func main() { numChan := make(chan struct{}) defer close(numChan) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for num := 1; num <= 26; num++ { numChan <- struct{}{} fmt.Printf("%d", num) } }() go func() { defer wg.Done() for ch := 'A'; ch <= 'Z'; ch++ { <-numChan fmt.Printf("%s", string(ch)) } }() wg.Wait()
題目描述,給定一個字符串使用兩個協(xié)程交替打印它。
如果老鐵們上面的拿到題都會了這道題不就是和第一道題是這個一模一樣的嗎?廢話不多說直接上代碼
func main() { chChan := make(chan struct{}) defer close(chChan) var wg = new(sync.WaitGroup) wg.Add(2) str := "hello world" N := len(str) go func() { defer wg.Done() for i := 0; i < N; i++ { chChan <- struct{}{} if i%2 == 0 { fmt.Println(string(str[i])) } } }() go func() { defer wg.Done() for i := 0; i < N; i++ { <-chChan if i%2 == 1 { fmt.Println(string(str[i])) } } }() wg.Wait() }
當(dāng)然也可以使用有緩沖的channel在這里鐵子們可以自行編寫上面寫的太多了。
題目描述使用三個協(xié)程分別打印A,B,C打印這個100次。
本題的難度和上面那幾個題完全是一個貨色,我們可以使用三個有緩沖的channel就可以達(dá)到目的了。具體細(xì)節(jié)請看代碼
package main import ( "fmt" "sync" ) func main() { Achan := make(chan struct{}, 1) Bchan := make(chan struct{}, 1) Cchan := make(chan struct{}, 1) defer close(Achan) defer close(Bchan) defer close(Cchan) Achan <- struct{}{} counter := 0 maxVal := 10 exitChan := make(chan struct{}) //用于退出 go func() { for { <-Achan if counter >= maxVal { exitChan <- struct{}{} break } fmt.Printf("%s ", "A") counter++ Bchan <- struct{}{} } }() go func() { for { <-Bchan if counter >= maxVal { exitChan <- struct{}{} break } fmt.Printf("%s ", "B") counter++ Cchan <- struct{}{} } }() go func() { for { <-Cchan if counter >= maxVal { exitChan <- struct{}{} break } fmt.Printf("%s ", "C") counter++ Achan <- struct{}{} } }() <-exitChan }
在這里需要注意的點(diǎn)是我們需要close掉這個管道當(dāng)達(dá)到臨界值時,主協(xié)程退出但是defer方法會執(zhí)行這個時候管道一關(guān)閉所有協(xié)程都會收到退出信號,另外兩個阻塞在那里的協(xié)程就會退出這樣就沒有這個協(xié)程泄漏了。
并發(fā)將多個文件合并到一個文件當(dāng)中
MergeFile 把多個文件合成一個文件,并發(fā)實現(xiàn)子協(xié)程優(yōu)雅退出。采用并發(fā)的方式
本題的解題思路同樣的也非常的簡單我們可以定義一個管道并發(fā)的讀取文件寫入到管道當(dāng)中然后再并發(fā)的寫入到文件當(dāng)中。非常的簡單
package main import ( "bufio" "fmt" "io" "os" "strconv" "sync" ) // MergeFile 把多個文件合成一個文件,并發(fā)實現(xiàn)子協(xié)程優(yōu)雅退出 var fileChan = make(chan string, 10000) var writeFish = make(chan struct{}) var wg sync.WaitGroup func readFile(fileName string) { fin, err := os.Open(fileName) if err != nil { fmt.Println(err.Error()) return } defer fin.Close() defer wg.Done() reader := bufio.NewReader(fin) for { line, err := reader.ReadString('\n') //注意已經(jīng)包含換行符了 if err != nil { if err == io.EOF { if len(line) > 0 { line += "\n" fileChan <- line } break } else { fmt.Println(err) break } } else if line == "\r\n" { fmt.Println("進(jìn)來") continue } else { fileChan <- line } } } func writeFile(fileName string) { fout, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { fmt.Println(err) return } defer fout.Close() defer func() { close(writeFish) }() writer := bufio.NewWriter(fout) //LOOP: // for { // select { // case <-readFish: // close(fileChan)//注意需要關(guān)閉因為已經(jīng)沒有人往里面寫了 // for line:=range fileChan{ // writer.WriteString(line) //讀取時候已經(jīng)包含換行符了 // } // break LOOP // case line := <-fileChan: // writer.WriteString(line) //讀取時候已經(jīng)包含換行符了 // } // // } for { if line, ok := <-fileChan; ok { if line != "\r\n" { writer.WriteString(line) } } else { break } } writer.Flush() //刷新 } func main() { wg.Add(3) for i := 1; i <= 3; i++ { fileName := "Dir/" + strconv.Itoa(i) go readFile(fileName) } go writeFile("Dir/merge") wg.Wait() close(fileChan) <-writeFish }
啟動一個協(xié)程生成100個數(shù)發(fā)送到ch2管道當(dāng)中,再啟動一個協(xié)程從ch2當(dāng)中取值然后計算平方將其放入ch3管道當(dāng)中主協(xié)程打印
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func f1(ch2 chan int) { defer wg.Done() for i := 0; i < 50; i++ { ch2 <- i } close(ch2) } func f2(ch3 chan int, ch2 chan int) { defer wg.Done() defer close(ch3) for x := range ch2 { ch3 <- x * x } } func main() { wg.Add(2) a := make(chan int, 50) b := make(chan int, 50) go f1(a) go f2(b, a) wg.Wait() for x := range b { fmt.Println(x) } }
關(guān)于“Golang協(xié)程常見面試題代碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。