Golang tutorial implementation of the non reentrant function

  • 2020-06-19 10:31:15
  • OfStack

Function function

The Go function does not support nesting, overloading, and default arguments

However, the following features are supported:

You don't have to declare a prototype Variable length parameter The return value more Named return value parameters Anonymous functions closure

preface

A non-reentrant function is a function that can be executed only once at any point in time, no matter how many times it is called and how many goroutines there are.

This article explains blocking non-reentrant functions and producing non-reentrant function implementations in golang.

Scenario use case

A service polls some conditions, monitoring 1 status per second. We want each state to be checked independently without blocking. The implementation might look like this:


func main() {
 tick := time.Tick(time.Second)
 go func() {
 for range tick {
  go CheckSomeStatus()
  go CheckAnotherStatus()
 }
 }()
}

We chose to run each status check in our goroutine so that CheckAnotherStatus() Don't wait for CheckSomeStatus() To complete.

Each check usually takes a very short time, and much less than one second. But if CheckAnotherStatus() It takes more than a second to run itself. What happens? There may be an unexpected network or disk delay that affects the execution time of the check.

Does it make sense to execute a function twice at the same time? If not, we want it to be non-reentrant.

Blocked, non - reentrant function

An easy way to prevent functions from running multiple times is to use sync.Mutex .

Assuming we only care about calling the function from the above loop, we can implement the lock from outside the function:


import (
 "sync"
 "time"
)

func main() {
 tick := time.Tick(time.Second)
 var mu sync.Mutex
 go func() {
 for range tick {
  go CheckSomeStatus()
  go func() {
  mu.Lock()
  defer mu.Unlock()
  CheckAnotherStatus()
  }()
 }
 }()
}

The above code guarantees that CheckAnotherStatus() Not performed by multiple iterations of the loop. Executed before CheckAnotherStatus() Any subsequent iteration of the loop will be blocked by the mutex.

The blocking solution has the following properties:

It ensures a lot of" CheckAnotherStatus() "As the number of iterations of the loop. Suppose 1 execution ". CheckAnotherStatus() "And subsequent iterations will result in requests to call the same function.

Yield, non-reentrant function

In our status check story, it might not make sense for the next 10 calls to pile up. 1 is stagnant CheckAnotherStatus() The execution is complete, all 10 calls are executed suddenly, sequentially, and possibly in the next 1 second, 10 identical checks in the same 1 second.

The other solution is to give in. One profitable solution is:

If it has been implemented," CheckAnotherStatus() Suspension of execution. Will run up to 1 time" CheckAnotherStatus() ". Compared to the number of loop iterations, the actual" CheckAnotherStatus() Fewer calls.

The solution is implemented in the following ways:


import (
 "sync/atomic"
 "time"
)

func main() {
 tick := time.Tick(time.Second)
 var reentranceFlag int64
 go func() {
 for range tick {
  go CheckSomeStatus()
  go func() {
  if atomic.CompareAndSwapInt64(&reentranceFlag, 0, 1) {
   defer atomic.StoreInt64(&reentranceFlag, 0)
  } else {
   return
  }
  CheckAnotherStatus()
  }()
 }
 }()
}

atomic.compareandswapint64(&reentranceFlag, 0, 1) Only in the reentranceFlag==0 true is returned with an atomic setting of 1. In this case, entry is allowed, and the function can be executed. reentranceFlag stays at 1 until CheckAnotherStatus() Done, it is reset. when CompareAndSwapInt64(...) When false is returned, this means that reentranceFlag!=0 , which means that the function has been executed by another goroutine. The code generates and silently exits the function.

conclusion

We chose to implement non-reentrant code outside of the function in question; We can implement it in the function itself. In addition, int32 is also sufficient for int64. Above is the content of this piece, we have what question can communicate in the message below the article.


Related articles: