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.
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.
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 go
to 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 channel
to communicate, and use to select
check 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 build
method return err
exits 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 errCh
have , 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 go
to run one goroutine
, but in real practice, there are still problems that need to be dealt with.
Original link: k8scat.com/posts/code-…