Multitask usage mode

Single task only

Global resource loading (initialization), public resource destruction in multitasking

// Initialization work
// 1. Through init method, it is only executed once, but cannot be called repeatedly
// Simulate global configuration
type AppConfig struct {
	Name string
	IP string
	Version string
}
 var appconfig *AppConfig

func init()  {
	appconfig=new(AppConfig)	
	appconfig.Name = "myapp"
	appconfig.IP = "192.168.66.88"
	appconfig.Version="v1.0"
}

// 2. Use the sync.once method to ensure that our code is executed once
// Destroy global resources
var once sync.Once
func DelAPP()  {
	once.Do(func(){
		appconfig = nil
		log.Println("delete app!")
	})
}

Writing asynchronous methods

If the task takes a long time, you can consider writing the method asynchronously (similar to ajax request in front-end knowledge), for example, if there is a task in the task that needs to be downloaded, then the download task can consider writing asynchronously, such as data storage task, etc

Key points of writing asynchronous task method: start goroutine to process the task within the task, and return immediately >

// Write a database asynchronous processing task (simulation)
func StorageData(data string){
    // Preprocessing data
    log.Println("Pretreatment before warehousing: ",data)
    go func(data){
        time.Sleep(time.Sencond*3)
        log.Println("Database storage complete")
    }
}

Write asynchronous callback method

Based on the asynchronous method, if you want to process the asynchronous result, you need to pass a callback method

Note: on the basis of writing asynchrony, add callback method (processing result)

// Writing an asynchronous web crawling method
package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

func CrawlUrl(url string,fn func(response *http.Response))  {
	go func() {
		response,err:=http.Get(url)
		if err!=nil{
			log.Println(err)
			return
		}
		fn(response)
	}()
}

func main() {

	CrawlUrl("http://www.baidu.com", func(response *http.Response) {
		data,err:=ioutil.ReadAll(response.Body)
		if err!=nil{
			return
		}
		log.Println("Content length: ",len(data))
	})
	log.Println("main func do other thing")

	CrawlUrl("http://i2.hdslb.com/bfs/archive/bc8adff1dafe7494c6b2155ec82725af0034c31b.png", func(response *http.Response) {
		data,err:=ioutil.ReadAll(response.Body)
		if err!=nil{
			log.Println(err)
			return
		}
		f,_:=os.Create("1.png")
		defer f.Close()
		_, _ = f.Write(data)
	})

	time.Sleep(time.Second*5)
}

 

Wait for all tasks to finish

Start multiple subtasks and wait for subtasks to finish

No results required

Need results

// 1. You need to return results, just complete the task
func CallTaskNoRsult(){
    wg := sync.WaitGroup{}
    wg.Add(5)
    for i:=0;i<5;i++{
        go func(num int) {
            defer wg.Done()
            log.Println("task",num,"complete")
        }(i)
    }
    wg.Wait()
}


// 2. You need to complete the task and return the result, which may need to be processed later
func CallTaskResults()  {
    // Create a channel to receive task processing results
    results:=make(chan string,10)

    defer close(results)
    // Random seed
    rand.Seed(time.Now().UnixNano())

    for i:=0;i<10;i++{
        go func(num int) {
            // Randomly generate a result and add the result to the result queue
            results<-fmt.Sprintf("Task %d# result: %d",num,rand.Intn(20)+10)
        }(i)
    }

    //Wait for output
    for i:=0;i<10;i++{
        log.Println(<-results)
    }
}

 

Wait for any task to complete

If there are multiple download sources and it is not sure which source network is in good condition, this method can be considered.

// Simulate multi-source download, and any task will be completed
func Download(){
	// Define a channel to receive results
	result:=make(chan string)

	defer close(result)

	// seed
	rand.Seed(time.Now().UnixNano())

	// Get download data of subtask asynchronously
	for i:=0;i<10;i++{
		go func(num int) {
			// Sleep randomly for a period of time
			time.Sleep(time.Second*time.Duration(rand.Intn(5)+1))
			result<-fmt.Sprintf("Task %d# download ok!",num)
		}(i)
	}

	// Wait for subtask to complete
	log.Println(<-result)

}

 

Collaboration waiting for other task results

Some businesses with a little complexity, one of which requires the results of another subtask

 

 

 

// Asynchronous collaboration, Task B needs task calculation results
func BWaitA()  {
	// Create channel for task communication
	ch:=make(chan string,1)  // Caching is 1
	defer close(ch)
	// Create a wait group
	wg:= sync.WaitGroup{}
	wg.Add(2)
	// Start task A
	go func() {
		defer wg.Done()
		log.Println("A do working...")
		time.Sleep(time.Second*3)
		log.Println("A end calc...., send result to B")
		ch<-"data for B"
		log.Println("A do send result to B, do other thing!")
	}()

	// Start task B
	go func() {
		defer wg.Done()
		log.Println("B do working...")
		time.Sleep(time.Second*2)
		log.Println("B wait A...")
		log.Println(<-ch)
		log.Println("B user A result do other thing!")
	}()
	wg.Wait()
}

Task cancellation

In some special situations, we may need to cancel the execution of subtasks, for example, the main task needs to be terminated in advance due to the user's reason, and all subtasks should be notified to end

Single level task cancellation

func CancleTask()  {
	// Create a cancel channel to notify the subtask to end
	canncle:=make(chan struct{})

	// Encapsulates a method to check whether the task has been cancelled
	iscancle:= func() bool {
		select {
		case <-canncle:
			return true
		default:
			return false
		}
	}
	//Simulation start subtask
	for i:=0;i<5;i++{
		go func(num int) {
			for{
        // Whether the listening task is cancelled
				if iscancle(){
					// Exit task if cancelled
					log.Printf("Task #%d is canceled!\n",num)
					return
				}
				log.Printf("Task #%d is working...\n",num)
                time.Sleep(time.Millisecond *50)
			}
		}(i)
	}
	// Simulation task runs for a period of time
	log.Println("main working for an while...")
	time.Sleep(time.Millisecond *100)

	// Cancel the task and turn off the broadcast feature of the channel
	close(canncle)

	time.Sleep(time.Second)
}

 

Multi level task cancellation

When the task is more complex, it may be related tasks, such as multi-level tasks as shown in the figure below

A preliminary understanding of Context

  • For the root context node, context. Backgoud() creates an empty context (root node), which has no effect. In order to inherit the

  • ctx, cancel: = context.withcancel (ctx), return a ctx child node, return a cancelfuc

  • Call cancel method to send cancel signal to all child nodes

  • Listen for cancel signal in subtask and exit the task

Cancel task with context

func CancelWithCtx()  {
	// Create empty ctx, root node
	root:=context.Background()

	// Define isCancel method
	isCanncel:= func(ctx context.Context) bool {
		select {
		case <-ctx.Done():
			return true
		default:
			return false
		}
	}

	Task:=func (ctx context.Context, num int){
		// Start subtask
		go func() {
			for{
				if isCanncel(ctx){
					// Exit task if cancelled
					log.Printf("Task #%d sub is canceled!\n",num)
					return
				}
			}
		}()
		for{
			if isCanncel(ctx){
				// Exit task if cancelled
				log.Printf("Task #%d is canceled!\n",num)
				return
			}
			log.Printf("Task #%d is working...\n",num)
			time.Sleep(time.Millisecond*200)
		}
	}
    // Create child ctx to cancel
	ctxOne,cancelOne:=context.WithCancel(root)
	go Task(ctxOne,1)
    
    // Create child ctx to cancel
	ctxTwo,_:=context.WithCancel(root)
	go Task(ctxTwo,2)

	// Simulation ensures that all goroutine s are running
	time.Sleep(time.Second)
    // Cancel first task
	cancelOne()
	time.Sleep(time.Second)
}

output:

2019/06/27 11:55:45 Task #1 is working...
2019/06/27 11:55:45 Task #2 is working...
2019/06/27 11:55:46 Task #1 is working...
2019/06/27 11:55:46 Task #2 is working...
2019/06/27 11:55:46 Task #1 is working...
2019/06/27 11:55:46 Task #2 is working...
2019/06/27 11:55:46 Task #1 is working...
2019/06/27 11:55:46 Task #2 is working...
2019/06/27 11:55:46 Task #1 is working...
2019/06/27 11:55:46 Task #2 is working...
2019/06/27 11:55:46 Task #1 sub is canceled!
2019/06/27 11:55:46 Task #1 is canceled!
2019/06/27 11:55:46 Task #2 is working...
2019/06/27 11:55:47 Task #2 is working...
2019/06/27 11:55:47 Task #2 is working...
2019/06/27 11:55:47 Task #2 is working...
2019/06/27 11:55:47 Task #2 is working...

 

The demonstration of context in net/http package

package main

import (
	"fmt"
	"net/http"
	"time"
)

func CtxTest(w http.ResponseWriter, r *http.Request){
	// Get ctx
	ctx := r.Context()

	fmt.Println("processing request")

	select {
	case <-time.After(5 * time.Second):
		// Simulation request processing completed
		w.Write([]byte("request processed"))
	case <-ctx.Done(): // When the web page is loaded, the Done signal is sent
		// When the user cancels, obtain the cancellation signal s
		fmt.Println( "request cancelled")
	}
}

func main() {
	// Binding route processing method
	http.HandleFunc("/",CtxTest)
	// Startup service
	http.ListenAndServe(":8000", nil)
}

Keywords: Go Database network

Added by stereo on Wed, 27 Nov 2019 11:43:13 +0200