SoFunction
Updated on 2025-03-04

Summary of common interview questions for Golang coroutine

Alternately print odd and even numbers

Let's take a look at the common algorithm interview questions in golang
Use two goroutines to alternately print odd and even numbers between 1-100, and output them from small to large.

Method 1: Use unbuffered channel for inter-coroutine communication

package main

import (
    "fmt"
    "sync"
)

// PrintOddAndEven1 /*
func PrintOddAndEven1() {
    //Method 1, use unbuffered channel for communication    var wg = new() //Note that here you need to be pointer in Go language, all value passing is required    (2)
    ch := make(chan struct{}) //Unbuffered channel    defer close(ch)
    maxVal := 100
    go func() {
        defer ()
        for i := 1; i <= maxVal; i++ {
            ch <- struct{}{}
            if i%2 == 1 { //odd number                ("the odd is %d\n", i)

            }
        }
    }()

    go func() {
        defer ()
        for i := 1; i <= maxVal; i++ {
            <-ch          //Read a data from the pipeline            if i%2 == 0 { //even                ("the even is %d\n", i)

            }
        }
    }()
    ()

}
func main() {
    PrintOddAndEven1()
    ("over")
}

The blogger will explain the principle of this below. First of all, because the variable ch is an unbuffered channel, it will not block only when reading and writing are ready at the same time. Therefore, two goroutines will enter their respective if statements at the same time (i is the same at this time), but only one if is true at this time. No matter whether the goroutine is fast, it will block due to reading channel or writing channel. Therefore, the program will alternately print 1-100 and have order.

Method 2: Use a buffered channel

func PrintOddAndEven2() {
    var wg = new() //Note that here you need to be pointer in Go language, all value passing is required    (2)
    oddChan := make(chan struct{}, 1)
    eveChan := make(chan struct{}, 1)
    defer close(oddChan)
    defer close(eveChan)
    oddChan <- struct{}{}
    maxVal := 20
    go func() { //Odd Co-Process        defer ()
        for i := 1; i <= maxVal; i += 2 {
            <-oddChan
            ("the odd print %d\n", i)
            eveChan <- struct{}{} //Notify even coroutine        }
    }()

    go func() {
        // Even coroutine        defer ()
        for i := 2; i <= maxVal; i += 2 {
            <-eveChan
            ("the even print %d\n", i)
            oddChan <- struct{}{} //Notify odd coroutine to print        }
    }()
    ()

}

func main() {
    PrintOddAndEven2()
    ("over")
}

The second method uses this buffered channel. When the capacity of the buffered channel does not reach the upper limit, write to the channel of the odd coroutine will not block. Here, the channel capacity of the odd coroutine is 1. We wrote a data to it in advance. Therefore, when both the even and odd coroutines start to read the data, the first thing to read the data is the odd coroutine. After the odd coroutine is printed, it is notified to print even coroutines. After the even coroutine is printed, it is notified to repeat the odd coroutines.

N coroutines print 1 to maxVal

The description of the question is very simple, N coroutines alternately print 1 to maxVal. For example, N=3, maxVal is the 100 effect. The effect should be the first coroutine print 1, the second coroutine print 2, the third coroutine print 3, and the first coroutine print 4.
This question looks very complicated. When the blogger saw this question for the first time, he felt it was very complicated, but if you think about it carefully, it is actually not that complicated. It is the same as the solution ideas of the above two questions. Let's see how this code is implemented

package main

import (
    "fmt"
    "sync"
)

func main() {
    maxVal := 10
    res := 0                        // Used to print numbers    N := 3                          //The number of coroutines    exitChan := make(chan struct{}) // Used to exit    chanArr := make([]chan struct{}, N)
    for i := 0; i < N; i++ {
        //Use unbuffered channel        chanArr[i] = make(chan struct{}, 1)
    }
    num := 0 //It's the turn of the coroutine to start printing    chanArr[0] <- struct{}{}
    for i := 0; i < N; i++ {
        go func(i int) {
            for {
                <-chanArr[i]
                if res >= maxVal {
                    exitChan <- struct{}{}
                    break
                }
                ("The %dth coroutine prints %d\n", i, res)
                if num == N-1 {//It's already a round of cycles that it's the 0th coroutine to print data                    num = 0
                } else {
                    num++
                }
                res++
                chanArr[num] <- struct{}{} //The num-th coroutine can print data
            }

        }(i)
    }
    <-exitChan
    for i := 0; i < N; i++ {
        close(chanArr[i]) //Close all the pipelines, otherwise there will be coroutine leaks    }

}

In fact, it is also very simple to use the channel to communicate between the coroutines. Since it is N coroutines, we define a channel slice to write one data into the first channel. The other pipeline does not write data. Then the first one must be the first coroutine. Then we use a counter to notify other coroutines to print. Finally, it is important to note that when the main coroutine exits, all the pipelines need to be closed. Otherwise, other coroutines will block there consistently, which will cause coroutine leakage, and you can only wait until gc to recycle.

Alternately print characters and numbers

Problem description: Use two goroutines to alternately print the sequence, one goroutinue to print the number, and the other goroutine to print the letters. The final effect is as follows: 12AB34CD56EF78GH910IJ.

If I have learned the above two questions, then this question is just a handy one and there is no difference between the first question and the first question

func main() {
    numChan := make(chan struct{}, 1)
    chChan := make(chan struct{}, 1)
    defer close(numChan)
    defer close(chChan)
    var wg 
    (2)
    numChan <- struct{}{}
    go func() {
        defer ()
        for num := 1; num <= 26; num++ {
            <-numChan
            ("%d", num)
            chChan <- struct{}{}
        }
    }()

    go func() {
        defer ()
        for ch := 'A'; ch <= 'Z'; ch++ {
            <-chChan
            ("%s", string(ch))
            numChan <- struct{}{}
        }
    }()
    ()

}

The same is true for communication using this channel and the buffered channel. Of course, this unbuffered channel can also be used for communication

func main() {
    numChan := make(chan struct{})
    defer close(numChan)
    var wg 
    (2)
    go func() {
        defer ()
        for num := 1; num <= 26; num++ {
            numChan <- struct{}{}
            ("%d", num)

        }
    }()

    go func() {
        defer ()
        for ch := 'A'; ch <= 'Z'; ch++ {
            <-numChan
            ("%s", string(ch))
        }
    }()
    ()

Alternately print strings

The title description is given a string to print it alternately using two coroutines.
If the old guys know the questions above and know what they get, wouldn’t this question be exactly the same as the first question? Just go to the code without saying much nonsense

Method 1 Use unbuffered channel

func main() {
    chChan := make(chan struct{})
    defer close(chChan)
    var wg = new()
    (2)

    str := "hello world"
    N := len(str)
    go func() {
        defer ()
        for i := 0; i < N; i++ {
            chChan <- struct{}{}
            if i%2 == 0 {
                (string(str[i]))
            }
        }
    }()

    go func() {
        defer ()
        for i := 0; i < N; i++ {
            <-chChan
            if i%2 == 1 {
                (string(str[i]))
            }
        }
    }()
    ()

}

Of course, you can also use buffered channels here. Irons can write too much on them.

Three coroutines print ABC

The title description uses three coroutines to print this 100 times respectively.
The difficulty of this question is exactly the same as the above questions. We can use three buffered channels to achieve our goal. For details, please refer to the code

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 &lt;- struct{}{}
    counter := 0
    maxVal := 10
    exitChan := make(chan struct{}) // Used to exit    go func() {
        for {
            &lt;-Achan
            if counter &gt;= maxVal {
                exitChan &lt;- struct{}{}
                break
            }
            ("%s ", "A")
            counter++
            Bchan &lt;- struct{}{}
        }
    }()

    go func() {
        for {
            &lt;-Bchan
            if counter &gt;= maxVal {
                exitChan &lt;- struct{}{}
                break
            }
            ("%s ", "B")
            counter++
            Cchan &lt;- struct{}{}
        }
    }()

    go func() {
        for {
            &lt;-Cchan
            if counter &gt;= maxVal {
                exitChan &lt;- struct{}{}
                break
            }
            ("%s ", "C")
            counter++
            Achan &lt;- struct{}{}
        }
    }()

    &lt;-exitChan
}

The point that needs to be noted here is that we need to close this pipeline and when the critical value is reached, the main coroutine exits, but the defer method will be executed. At this time, once the pipeline closes all coroutines, it will receive an exit signal, and the other two coroutines blocking there will exit, so that there will be no leakage of this coroutine.

Concurrently merge multiple files into one file

MergeFile synthesizes multiple files into one file and concurrently implements the elegant exit of the child coroutine. Adopt concurrent
The solution to this problem is also very simple. We can define a pipeline to read the file concurrently and write it into the pipeline and then write it concurrently into the file. Very simple

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strconv"
    "sync"
)

// MergeFile combines multiple files into one file and implements subcoroutines elegantly exit
var fileChan = make(chan string, 10000)
var writeFish = make(chan struct{})
var wg 

func readFile(fileName string) {
    fin, err := (fileName)
    if err != nil {
        (())
        return
    }

    defer ()
    defer ()
    reader := (fin)
    for {
        line, err := ('\n') //Note that line breaks are already included        if err != nil {
            if err ==  {
                if len(line) &gt; 0 {
                    line += "\n"
                    fileChan &lt;- line
                }
                break
            } else {
                (err)
                break
            }
        } else if line == "\r\n" {
            ("Come in")
            continue
        } else {
            fileChan &lt;- line
        }
    }

}
func writeFile(fileName string) {
    fout, err := (fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
    if err != nil {
        (err)
        return
    }
    defer ()
    defer func() {
        close(writeFish)
    }()
    writer := (fout)
    //LOOP:
    //    for {
    //        select {
    //        case &lt;-readFish:
    //                close(fileChan)// Be careful to close because no one has written it in it    //            for line:=range fileChan{
    //    //            }
    //            break LOOP
    //        case line := &lt;-fileChan:
    //             (line) //The newline character is already included when reading    //        }
    //
    //    }
    for {
        if line, ok := &lt;-fileChan; ok {
            if line != "\r\n" {
                (line)
            }
        } else {
            break
        }
    }
    () //refresh
}

func main() {
    (3)
    for i := 1; i &lt;= 3; i++ {
        fileName := "Dir/" + (i)
        go readFile(fileName)
    }

    go writeFile("Dir/merge")
    ()
    close(fileChan)
    &lt;-writeFish

}

Channel Exercise

Start a coroutine to generate 100 numbers and send them to the ch1 pipeline, and then start a coroutine to take the value from ch1 and then calculate the square and put it into the ch2 pipeline to print the main coroutine

package main

import (
    "fmt"
    "sync"
)

var wg 

func f1(ch1 chan int) {
    defer ()
    for i := 0; i < 50; i++ {
        ch1 <- i
    }
    close(ch1)
}
func f2(ch2 chan int, ch1 chan int) {
    defer ()
    defer close(ch2)
    for x := range ch1 {
        ch2 <- x * x
    }
}
func main() {
    (2)
    a := make(chan int, 50)
    b := make(chan int, 50)
    go f1(a)
    go f2(b, a)
    ()
    for x := range b {
        (x)
    }

}

This is the end of this article about the summary of common interview questions for Golang Coroutine. For more relevant interview questions for Golang Coroutine, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!