Go language learning programming practice: five modes to solve concurrency problems in go

For-Select-Done

We should prevent any leaks in the program. So we should signal the go routine left in the program to let it know that it can exit.
The most common is to combine a for-select loop with a channel to send a close signal to the go program. We call this the "Complete" channel.

func printIntegers(done <-chan struct{
    
    }, intStream <-chan int) {
    
    
	for{
    
    
		 select {
    
    
		 case i := <-intStream:
		 fmt.Println(i)
		 case <-done:
		 return
		 }
	 }
}

If you don't understand it, remember it here...

fan-in mode

insert image description here

func fanIn(ctx context.Context, fetchers ...<-chan interface{
    
    }) <-chan interface{
    
    } {
    
    
 combinedFetcher := make(chan interface{
    
    })
 // 1
 var wg sync.WaitGroup
 wg.Add(len(fetchers))
 
 // 2
 for _, f := range fetchers {
    
    
 f := f
 go func() {
    
    
 // 3
 
 defer wg.Done()
 for{
    
    
 select{
    
    
 case res := <-f:
 combinedFetcher <- res
 case <-ctx.Done():
 return
 }
 }
 }()
 }
 
 // 4
 // Channel cleanup
 go func() {
    
    
 wg.Wait()
 close(combinedFetcher)
 } ()
 return combinedFetcher
}

get the first n values ​​from the stream

insert image description here

func takeFirstN(ctx context.Context, dataSource <-chan interface{
    
    }, n int) <-chan interface{
    
    } {
    
    
 // 1
 takeChannel := make(chan interface{
    
    })
 
 // 2
 go func() {
    
    
 defer close(takeChannel)
 // 3 
 for i := 0; i< n; i++ {
    
    
 select {
    
    
 case val, ok := <-dataSource:
 if !ok{
    
    
 return
 }
 takeChannel <- val
 case <-ctx.Done():
 return
 }
 }
 }()
 return takeChannel
}

subscription model

type Subscription interface {
    
    
 Updates() <-chan Item
}
//On the other hand, we are going to use another interface as an abstraction to fetch the data we 
//need:
//另一方面,我们将使用另一个接口作为抽象来获取我们需要的数据。
type Fetcher interface {
    
    
 Fetch() (Item, error)
}
//For each of these we are going to have a concrete type implementing them.
//对于其中的每一个,我们都将有一个具体的类型来实现它们。
//For the subscription:
//对于订阅来说。
func NewSubscription(ctx context.Context, fetcher Fetcher, freq int) Subscription {
    
    
 s := &sub{
    
    
 fetcher: fetcher,
 updates: make(chan Item),
 }
// Running the Task Supposed to fetch our data
 go s.serve(ctx, freq)
 return s
}
type sub struct {
    
    
 fetcher Fetcher
 updates chan Item
}
func (s *sub) Updates() <-chan Item {
    
    
 return s.updates
}
//We are going to go into more details about what happens inside the serve method.
我们将更详细地介绍服务方法内部发生的事情。
//For the fetcher:
//对于取物者来说。
func NewFetcher(uri string) Fetcher {
    
    
 f := &fetcher{
    
    
 uri: uri,
 }
 return f
}
type fetcher struct {
    
    
 uri string
}
//Inside the serve method

//The serve method consists of a for-select-done type of loop:
//服务方法由一个 for-select-done 类型的循环组成。
func (s *sub) serve(ctx context.Context, checkFrequency int) {
    
    
 clock := time.NewTicker(time.Duration(checkFrequency) * time.Second)
 type fetchResult struct {
    
    
 fetched Item
 err error
 }
 fetchDone := make(chan fetchResult, 1)
 
 for {
    
    
 select {
    
    
 // Clock that triggers the fetch
 case <-clock.C:
 go func() {
    
    
fetched, err := s.fetcher.Fetch()
fetchDone <- fetchResult{
    
    fetched, err}
 }()
 // Case where the fetch result is
 // Ready to be consumed
 case result := <-fetchDone:
 fetched := result.fetched
 if result.err != nil {
    
    
 log.Println("Fetch error: %v \n Waiting the next iteration", result.err.Error())
break
 }
 s.updates <-fetched
 // Case where we need to close the server
 case <-ctx.Done():
 return
 }
 }
}

There is a correction, which I will not mention for now.

map mode

func Map(done <-chan struct{
    
    }, inputStream <-chan int, operator func(int)int) <-chan int {
    
    
 // 1
 mappedStream := make(chan int)
 go func() {
    
    
 defer close(mappedStream)
 // 2
 for {
    
    
 select {
    
    
 case <-done:
 return
 // 3
 case i, ok := <-inputStream:
 if !ok {
    
     return }
 //4
 select {
    
    
 case <-done:
 return
 case mappedStream <- operator(i):
 }
 }
 }
 }()
 return mappedStream
func main() {
    
    
 done := make(chan struct{
    
    })
 defer close(done)
 
 // Generates a channel sending integers
 // From 0 to 9
 range10 := rangeChannel(done, 10)
 
 multiplierBy10 := func(x int) int {
    
    
 return x * 10
 }
 for num := range Map(done, range10, multiplierBy10) {
    
    
 fmt.Println(num)
 }
}

filter mode

insert image description here

func Filter(done <-chan struct{
    
    }, inputStream <-chan int, operator func(int)bool) <-chan int {
    
    
 filteredStream := make(chan int)
 go func() {
    
    
 defer close(filteredStream)
 
 for {
    
    
 select {
    
    
 case <-done:
 return
 case i, ok := <-inputStream:
 if !ok {
    
    
 return
 }
 
 if !operator(i) {
    
     break }
 select {
    
    
 case <-done:
 return
 case filteredStream <- i:
 }
 }
 }
 }()
 return filteredStream
}
func main() {
    
    
 done := make(chan struct{
    
    })
 defer close(done)
 
 // Generates a channel sending integers
 // From 0 to 9
 range10 := rangeChannel(done, 10)
 
 isEven := func(x int) bool {
    
    
 return x % 2 == 0
 }
 for num := range Filter(done, range10, isEven) {
    
    
 fmt.Println(num)
 }
}

These five patterns are the building blocks for building larger and more complex Golang applications. These solutions solve
problems you may encounter when dealing with concurrency problems in GO.
In addition, you can modify, extend and create new patterns based on it .

Guess you like

Origin blog.csdn.net/weixin_43786143/article/details/129401662