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