Golang Concurrent Programming Practice | Go Theme Month

Humans are a highly concurrent species, delicate.

first acquaintance

The first impression of the Go language is that it supports concurrent programming natively, and uses coroutines, which are more lightweight than threads.

ybk93cwmg3y51.jpg

The difference between processes, threads and coroutines

  • A process is "an instance of program execution" that acts as an entity that allocates system resources. Process creation must allocate a complete separate address space. Process switching only happens in kernel mode.
  • Thread: A thread is an execution flow of a process that independently executes its own program code. It is the smallest unit of program execution flow and the basic unit of processor scheduling and dispatch. A process can have one or more threads.
  • Coroutines: Coroutines are not processes or threads, and their execution is more like a subroutine, or a function call without a return value. Concurrent coroutines can be created at the language level and then written to manage them. Go outsources this step to make coroutines less expensive to run concurrently.

a.jpeg

Go implements the simplest concurrency

for i := 0; i < 10; i++ {
    go func(n int) {
        fmt.Println(n)
    }(i)
}
复制代码

Project Practice

Recently, a project needs to call multiple jobs at the same time, and wait for these jobs to complete before executing.

Execute job serially

Initially, we have a method that executes the job, and executes all the jobs serially:

func buildJob(name string) {
    ...
}

buildJob("A")
buildJob("B")
buildJob("C")
复制代码

Execute jobs in parallel

Because all jobs can be executed concurrently, there is no need to wait for the execution of the previous job to complete before continuing to execute other jobs. We can use the Go keyword goto quickly enable one goroutine, below we will execute three jobs concurrently:

go buildJob("A")
go buildJob("B")
go buildJob("C")
复制代码

wait for all jobs to finish

How to know if each job has been completed, here you can use channelto communicate, and use to selectcheck the execution result:

func buildJob(ch chan error, name string) {
    var err error
    
    ... // build job
    
    ch <- err // finnaly, send the result into channel
}

func build() error {
    jobCount := 3
    errCh := make(err chan error, jobCount)
    defer close(errCh) // 关闭 channel

    go buildJob(errCh, "A")
    go buildJob(errCh, "B")
    go buildJob(errCh, "C")

    for {
        select {
        case err := <-ch:
            if err != nil {
                return err
            }
        }
        
        jobCount--
        if jobCount <= 0 {
            break
        }
    }
    
    return nil
}
复制代码

problem found

When job A fails, the buildmethod return errexits and executes close(errCh). However, at this time, the other two jobs B and C may not be executed yet, and the results will be sent to them at the same time errCh, but since they errChhave , the program will exit panic: send on closed channel.

optimize code

When sending data to the channel, you can use the second value of the received data to determine whether the channel is closed:

func buildJob(ch chan error, name string) {
    var err error
    
    ... // build job
    
    if _, ok := <-ch; !ok {
        return
    }
    ch <- err // finnaly, send the result into channel
}

func build() error {
    jobCount := 3
    errCh := make(err chan error, jobCount)
    defer close(errCh) // 关闭 channel

    go buildJob(errCh, "A")
    go buildJob(errCh, "B")
    go buildJob(errCh, "C")

    for {
        select {
        case err := <-ch:
            if err != nil {
                return err
            }
        }
        
        jobCount--
        if jobCount <= 0 {
            break
        }
    }
    
    return nil
}
复制代码

Summarize

Go concurrent programming seems to only need one keyword goto run one goroutine, but in real practice, there are still problems that need to be dealt with.

Original link: k8scat.com/posts/code-…

Guess you like

Origin juejin.im/post/6944362687811813412