Concepts#
- Concurrency is not parallelism: Concurrency does not equal parallelism
- There are different types of channels, with the <- and -> arrows indicating the direction of the channel
- The channel's buffer size indicates whether the channel is synchronous or asynchronous
- CAS: compare-and-swap: CAS technology is used in designing concurrent algorithms, improving the safety of multi-threaded execution
CAS#
CAS Principle: CAS has three operands: memory value V, expected old value A, and value B to be modified. It modifies the memory value to B and returns true only when the expected value A is equal to the memory value V; otherwise, it does nothing and returns false.
- The CAS operation is provided by the operating system with corresponding interfaces and supported at the hardware level
if *addr == old {
*addr = new
return true
}
return false
- CAS Golang implementation of lock
package main
import (
"sync/atomic"
)
type Mutex struct {
state int32
}
func (m *Mutex) Lock() {
for(!atomic.CompareAndSwapInt32(&m.state, 0, 1)) {
return
}
}
func (m *Mutex) Unlock() {
atomic.CompareAndSwapInt32(&m.state, 1, 0)
}
- Implementing sync.Map based on CAS: goroutine thread safety
Beginner#
Generator#
In Golang, channels are first-class types just like basic variables such as int and string. A generator is a function that returns a channel, adding items to the channel internally.
The generator pattern is commonly used to implement the simplest producer-consumer model or publish-subscribe model.
func boring(msg string) <-chan string { // Returns receive-only channel of strings.
c := make(chan string)
go func() { // We launch the goroutine from inside the function.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return the channel to the caller.
}
Reusing Channels (select)#
func main() {
c := boring("Joe")
timeout := time.After(5 * time.Second)
timer := time.NewTimer(3 * time.Second)
for {
select {
case s := <-c:
fmt.Println(s)
case <-timer.C:
// Scheduled polling task
timer.Reset(3 * time.Second)
case <-timeout: // Listen for timeout signal
fmt.Println("You talk too much.")
return
case <-quit: // Listen for exit signal
return
}
}
}
Controlling Concurrency (WaitGroup->chan->context)#
WaitGroup#
WaitGroup is a basic class for controlling concurrency
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
time.Sleep(2*time.Second)
fmt.Println("Worker 1 completed")
wg.Done()
}()
go func() {
time.Sleep(2*time.Second)
fmt.Println("Worker 2 completed")
wg.Done()
}()
wg.Wait()
fmt.Println("Alright, everyone is done, time to go home")
}
Context#
The context has the following 4 functions to derive a new context from the parent context.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
- WithCancel: returns ctx and a cancel function
- WithDeadline: returns ctx and a cancel function, automatically cancels when the time exceeds the deadline
- WithTimeout: similar to WithDeadline, but for timeout duration
- WithValue: passes metadata, obtained via ctx.Value(key)
Context is the context, as shown in the following code to construct a context with a cancel function, meaning that after executing the cancel method, the watch function responds to exit.
Context is typically used to handle each goroutine of request requests.
func main() {
ctx, cancel := context.WithCancel(context.Background())
go watch(ctx,"【Monitor 1】")
go watch(ctx,"【Monitor 2】")
go watch(ctx,"【Monitor 3】")
time.Sleep(10 * time.Second)
fmt.Println("Alright, notifying monitors to stop")
cancel()
// To check if the monitors have stopped, if there is no monitor output, it indicates that it has stopped
time.Sleep(5 * time.Second)
}
func watch(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name,"monitor exited, stopped...")
return
default:
fmt.Println(name,"goroutine monitoring...")
time.Sleep(2 * time.Second)
}
}
}
Worker Scheduling#
Basic worker queue scheduling method
idleWorker<-workerQueue
func(){
idleWorker.DoTask()
defer workerQueue<-idleWorker
}
Reference#
Golang Concurrency Programming
This message is used to verify that this feed (feedId:79713081934950400) belongs to me (userId:56537166843314176). Join me in enjoying the next generation information browser https://follow.is.