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")
}