Go語言併發之道學習八 context

goroutine中使用 context包

package main

import (
	"time"
	"sync"
	"fmt"
	"context"
)

type Context interface {
	//當爲該context 工作的work被取消時,deadline 將返回時間。在沒有設定期限的情況下,
	//deadline 返回 ok == false. 聯繫的調用deadline 返回相同的結果
	Deadline() (deadline time.Time,ok bool)

	//當爲該context 工作的work完成時,返回一個關閉的channel 。
	//如果這個context 不能被取消,那麼Done 可能返回nil 。連續調用完成返回相同的值
	Done() <-chan struct{}

	//Err 在完成後返回一個 non-nil 值。如果context 被取消,或者在context 的deadline結束時,
	//如果context 被取消,Err 將被取消。沒有定義Err 的其他值。連續調用結束後,用Err 返回相同的值
	Err() error

	//連續調用具有相同key 的值將返回相同的結果
	Value(key interface{}) interface{}
}


func main() {
	fmt.Println("sayhelloandbye")
	sayhelloandbye()
	fmt.Println()
	fmt.Println("contexthelloandbye")
	contexthelloandbye()
	fmt.Println()
	fmt.Println("ctxdeadline")
	ctxdeadline()
	fmt.Println()
	fmt.Println("ctxvalue")
	ctxvalue()
}

//同時打印問候和告別
func sayhelloandbye(){
	locale := func(done <-chan interface{}) (string,error){
		select {
		case <- done:
			return "",fmt.Errorf("canceled")
		case <-time.After(500*time.Millisecond):
		}
		return "EN/US",nil
	}

	genGreeting := func(done <-chan interface{}) (string,error) {
		switch locale,err := locale(done); {
		case err != nil:
			return "",err;
		case locale == "EN/US":
			return "hello",nil;
		}
		return "",fmt.Errorf("unsupported locale")
	}

	genFarewell := func(done <-chan interface{}) (string,error){
		switch locale,err := locale(done); {
		case err != nil:
			return "",err;
		case locale == "EN/US":
			return "goodbye",nil;
		}
		return "",fmt.Errorf("unsupported locale")
	}

	printGreeting := func(done <-chan interface{}) error {
		greeting , err := genGreeting(done)
		if err != nil{
			return err
		}
		fmt.Printf("%s world!\n",greeting)
		return nil
	}

	printFarewell := func(done <-chan interface{}) error {
		farewell,err := genFarewell(done)
		if err != nil {
			return err
		}
		fmt.Printf("%s world!\n",farewell)
		return nil
	}


	var wg sync.WaitGroup
	done := make(chan interface{})
	defer close(done)

	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printGreeting(done);err != nil{
			fmt.Printf("%v",err)
			return
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printFarewell(done); err != nil{
			fmt.Printf("%v",err)
			return
		}
	}()

	wg.Wait()

}

//context包同步問候和告別
func contexthelloandbye(){

	locale := func(ctx context.Context) (string,error) {
		select {
		case <-ctx.Done():
			return "",ctx.Err()
		case <-time.After(1*time.Minute):
		}
		return "EN/US",nil
	}

	genFarewell := func(ctx context.Context) (string,error) {
		switch local,err := locale(ctx); {
		case err != nil:
			return "",err;
		case local == "EN/US":
			return "goodbye",nil
		}
		return "",fmt.Errorf("unsupported locale")
	}

	genGreeting := func(ctx context.Context) (string,error) {
		ctx,cancel := context.WithTimeout(ctx,500*time.Millisecond)
		defer cancel()
		switch local,err := locale(ctx); {
		case err != nil:
			return "",err;
		case local == "EN/US":
			return "hello",nil
		}
		return "",fmt.Errorf("unsupported locale")
	}

	printGreeting := func(ctx context.Context) error {
		greeting,err := genGreeting(ctx)
		if err != nil{
			return err
		}
		fmt.Printf("%s world!\n", greeting)
		return nil
	}

	printFarewell := func(ctx context.Context) error {
		farewell,err := genFarewell(ctx)
		if err != nil{
			return err
		}
		fmt.Printf("%s world\n", farewell)
		return nil
	}

	var wg sync.WaitGroup
	ctx,cancel := context.WithCancel(context.Background())
	defer cancel()

	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printGreeting(ctx);err != nil{
			fmt.Printf("cannot print greeting: %v\n",err)
			cancel()
		}
	}()
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printFarewell(ctx);err != nil{
			fmt.Printf("cannot print farewell: %v\n",err)
		}
	}()
	wg.Wait()

	/*
	·ctx,cancel := context.WithCancel(context.Background())
	這裏main 用context.BackGround() 創建一個Context ,並用context.WithCancel 包裝它以允許取消

	·cancel()
	在這裏,如果從打印問候語返回錯誤,main 將取消上下文.

	·ctx,cancel := context.WithTimeout(ctx,500*time.Millisecond)
	這裏genGreeting 用context.WithTimeout 包裝它的 Context
	這將會在 500 毫秒後自動取消返回的上下文,從而取消它傳遞上下文的任何子函數,即語言環境

	·return "",ctx.Err()
	這一行返回爲什麼Context 被取消的原因,這個錯誤會一直冒泡到main ,這會導致main 的sync.WaitGroup 也取消。

	*/

}

//使用context deadline
func ctxdeadline(){
	locale := func(ctx context.Context) (string,error) {
		if deadline,ok := ctx.Deadline();ok{
			if deadline.Sub(time.Now().Add(1*time.Minute)) <= 0{
				return "",context.DeadlineExceeded
			}
		}
		select {
		case <-ctx.Done():
			return "",ctx.Err();
		case <-time.After(1*time.Minute):
		}
		return "EN/US",nil
	}

	genFarewell := func(ctx context.Context) (string,error) {
		switch local,err := locale(ctx); {
		case err != nil:
			return "",err
		case local == "EN/US":
			return "goodbye",nil;
		}
		return "",fmt.Errorf("unsupported locale")
	}

	genGreeting := func(ctx context.Context) (string,error) {
		ctx,cancel := context.WithTimeout(ctx,500*time.Millisecond)
		defer cancel()
		switch local,err := locale(ctx); {
		case err != nil:
			return "",err;
		case local == "EN/US":
			return "hello",nil
		}
		return "",fmt.Errorf("unsupported locale")
	}

	printGreeting := func(ctx context.Context) error {
		greeting,err := genGreeting(ctx)
		if err != nil {return err}
		fmt.Printf("%s world!\n",greeting)
		return nil
	}

	printFarewell := func(ctx context.Context) error{
		farewell , err := genFarewell(ctx)
		if err != nil{ return err}
		fmt.Printf("%s world!\n", farewell)
		return nil
	}

	var wg sync.WaitGroup

	ctx,cancel := context.WithCancel(context.Background())
	defer cancel()

	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printGreeting(ctx); err != nil{
			fmt.Printf("cannot print greeting: %v\n", err)
			cancel()
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := printFarewell(ctx);err != nil{
			fmt.Printf("cannot print farewell: %v\n",err)
		}
	}()

	wg.Wait()

	/*
	·if deadline,ok := ctx.Deadline();ok{
	在這裏我們檢查我們的上下文是否提供了截止。
	如果確實如此,並且我們的系統時鐘已超過截止時間,那麼我們只會返回上下文包中定義的特定錯誤,即DeadlineExceeded
	*/
}

//context value
func ctxvalue(){
	HandleResponse := func(ctx context.Context) {
		fmt.Printf(
			"handling response for %v (%v)\n",
			ctx.Value("userID"),
			ctx.Value("authToken"),
			)
	}

	ProcessRequest := func(userID,authToken string) {
		ctx := context.WithValue(context.Background(),"userID",userID)
		ctx = context.WithValue(ctx,"authToken",authToken)
		HandleResponse(ctx)
	}

	ProcessRequest("jane","abc123")

	/*
	你使用的鍵值必須滿足 golang 的可比性概念,也就是運算符 == 和 != 在使用時需要返回正確的結果

	返回值必須按期,才能從多個goroutine 訪問
	*/

	//下面我們自定義鍵類型,防止上下文的衝突
	type ctxKey int

	const(
		ctxUserID ctxKey = iota
		ctxAuthToken
	)

	UserID := func(c context.Context) string{
		return c.Value(ctxUserID).(string)
	}

	AuthToken := func(c context.Context) string {
		return c.Value(ctxAuthToken).(string)
	}

	HandleResponse2 := func(ctx context.Context) {
		fmt.Printf(
			"handling response for %v (auth:%v)\n",
			UserID(ctx),
			AuthToken(ctx),
			)
	}

	ProcessRequest2 := func(userID,authToken string){
		ctx := context.WithValue(context.Background(),ctxUserID,userID)
		ctx = context.WithValue(ctx,ctxAuthToken,authToken)
		HandleResponse2(ctx)
	}

	ProcessRequest2("make","efg321")
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章