Go EP.9 Go Context

27 Oct 2021,
Share: 
Cover image

สวัสดีครับ ในบทความนี้ก็เป็น EP.9 แล้วนะครับ โดยเนื้อหาจะเป็นเรื่องเกี่ยวกับ Context ในภาษา Go ซึ่งเป็นเรื่องที่เราจะต้องได้เจอเมื่อเราพัฒนาโปรแกรมด้วยภาษา Go ครับ

สําหรับท่านใดที่ยังไม่ได้อ่าน EP.8 ท่านสามารถกลับไปอ่านก่อนได้นะครับที่นี่ Go EP.8 Go Channel Select Multiple Communication Operations

มาเริ่มเรียนรู้ไปด้วยกันตามหัวข้อด้านล่างเลยครับ

Go Context คืออะไร

Context ใน Go จะช่วยให้เราสามารถควบคุมและ จัดการ การทํางานของ Process ต่างๆ ในการทํางานแบบ Multitasking เช่น กําหนด timeout, กําหนด deadline หรือ การส่งข้อมูลผ่าน channel เพื่อ Share data ระหว่าง Process ต่างๆ

เพื่อให้เข้าใจมากขึ้นผมจะยกตัวอย่างที่ใช้กันบ่อยๆคือ กรณีที่เรา Request ไปเอาข้อมูลจาก API แล้วฝั่ง Server ทํางานช้าเกินกว่าที่เรากําหนด และเราไม่ต้องการที่จะรอ เราจึงต้องทําอะไรบางอย่างเพื่อที่จะหยุดการทํางานนั้นซะ เช่น การกําหนด Timeout เพื่อประสิทธิภาพโดยรวมของระบบ นี่แหละครับ Context จะมาช่วยเราในการจัดการเรื่องพวกนี้

การใช้งาน Go Context

ในการใช้งาน Go Context เราจะเริ่มด้วยการสร้าง Parent Context ด้วย function ต่างๆดังนี้ครับ

context.Background()

Function นี้จะ Return context ว่างๆ (Empty context) ออกมา โดยปกติจะใช้ใน Main function, initialization, test และ top-level Context ซึ่งเราสามารถสร้าง Context ใหม่ที่สืบทอดจาก Parent Context นี้ได้ (เดี๋ยวเราจะเรียนรู้กันในหัวข้อถัดไปครับ)

ctx := context.Background()

context.TODO()

Function นี้จะ Return context ว่างๆ (Empty context) ออกมา เหมือนกันกับ context.Background() ทุกประการ แต่ส่วนใหญ่ context.TODO() จะใช้ในกรณีที่เรายังไม่มีการใช้งาน Context แต่ Function มีการรับ Paramiter context ครับ ตัวอย่างการสร้างก็จะประมาณนี้ครับ

ctx := context.TODO()

สรุปคือทั้ง context.Background() และ context.TODO() คือการสร้าง Empty context ออกมาเหมือนกันครับ ใช้อันไหนก็ได้

Parent context ที่ได้จะมี Type เป็น Context interface โดยภายในจะประกอบไปด้วย Method ตามนี้ครับ

type Context interface {
	Deadline() (deadline time.Time, ok bool)

	Done() <-chan struct{}

	Err() error

	Value(key interface{}) interface{}
}

หลังจากที่เรารู้วิธีการสร้าง Parent Context แล้ว ขั้นตอนต่อไปเราจะไปเรียนรู้การการนํา Parent context ไปสร้างเป็น Context ใหม่ด้วยวิธีสืบทอดมาจาก Parent context ซึ่งการสร้าง 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

จะเห็นว่าในแต่ละแบบจะ Return new context ออกมา ซึ่ง Context ที่ Return ออกมานั้นจะถูก Implement method ต่างๆภายในออกมาด้วย แต่การ Implement method ก็จะแตกต่างกันไปในแต่ละ Function ครับ

มาดูรายละเอียดของแต่ละ Function ดังนี้ครับ

context.WithCancel(parent Context)

WithCancel จะ Return new context และ Function cancel ออกมาให้ โดยที่ New context ที่ได้จะถูก Implement method Done() ออกมาให้เราด้วย ซึ่งเราจะใช้ Done() ในการควบคุมการทํางานอีกทีครับ ลองดูตัวอย่างการใช้งานตามนี้ครับ

package main

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

func main() {
	messageCh := make(chan string)
	ctx, cancel := context.WithCancel(context.Background())

	go task(ctx, messageCh)

	select{
	case message := <-messageCh:
		fmt.Println(message)
	case <-time.After(2 * time.Second): // ลองเปลี่ยนเวลาตรงนี้ครับ ถ้าน้อยกว่าเวลาใน task จะทําให้ task ถูก Cancel
		cancel()
		fmt.Println(<-messageCh)
	}
}

func task(ctx context.Context, ch chan string) {
	select{
	case <- time.After(3 * time.Second):
		ch <- "Do task success."
	case <- ctx.Done():
		ch <- "Cancel task."
	}
}

// Result
// Cancel task.

จาก Code ด้านบน ผมสร้าง Function task สําหรับจําลองการทําอะไรสักอย่าง โดยกําหนด Delay time ไว้ และใน Function main จะมี Delay 2 วินาทีก่อนเรียก Function cancel() ซึ่ง Delay ใน Function main น้อยว่า Delay ใน Function task ทําให้ task ถูก Cancel ก่อนที่จะทํางานเสร็จ ผลของการรันจะได้เป็น “Cancel task.”

แต่ถ้าเรากําหนดให้ Delay ใน Function main มากกว่า Delay ใน Function task จะได้ผลเป็นเป็น “Do task success.” เนื่องจาก Delay น้อยกว่า ทํางานเสร็จก่อนที่จะ Cancel ครับ

WithDeadline(parent Context, deadline time.Time)

WithDeadline จะเหมือนกับ WithCancel เลยครับ แต่จะมีสิ่งที่เพื่มเติมเข้ามาคือ เราสามารถกําหนด Deadline ได้ หมายความว่า เมื่อถึงกําหนด Deadline แล้ว Done() channel จะถูก Close ทันที ลองดูตามตัวอย่างนี้ครับ

package main

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

const shortDuration = 1 * time.Second

func main() {
	d := time.Now().Add(shortDuration)
	ctx, cancel := context.WithDeadline(context.Background(), d)

	defer cancel()

	select {
	case <-time.After(3 * time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}

}

// Result
// context deadline exceeded

จาก code ด้านบน เมื่อถึงกําหนด Deadline ก็จะทําให้ ctx.Done() close และก็แสดงผล ctx.Err() ออกมา นั้นก็คือ “context deadline exceeded” ครับ

context.WithTimeout(parent Context, timeout time.Duration)

WithTimeout ก็เหมือนกับ WithDeadline ทุกประการ แค่ต่างกันนิดนึงครับ ตรงที่ WithDeadline จะเป็นการกําหนด Deadline หรือ Expire time แต่ WithTimeout จะเป็นการกําหนด เป็น Timeout แทนครับ เพื่อให้เข้าใจมากขึ้น ลองดูตามตัวอย่างนี้ครับ

package main

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

const shortDuration = 1 * time.Millisecond

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err()) // prints "context deadline exceeded"
	}

}

// Result
// context deadline exceeded

จาก Code ด้านบนจะเห็นว่าการทํางานเหมือนกันกับ WithDeadline แต่จะต่างกันตรงที่ WithTimeout จะกําหนดเป็นระยะเวลา Timeout แทนครับ

context.WithValue(parent Context, key, val interface{})

สําหรับ WithValue จะต่างจาก Function อื่นๆ ครับ โดย WithValue จะไม่ return Function cancel มาให้ และการใช้งานจะเป็นการกําหนด Key และ Value เข้าไปใน Context ซึ่งส่วนใหญ่จะใช้ในการ Share ข้อมูลกันระหว่าง Function หรือ thread ต่างๆ

มาดูตัวอย่างกันครับ

package main

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

func main() {
	ctx := context.WithValue(context.Background(), "id", "A001")

	Get(ctx, "id")

	go func() {
		ctx2 := context.WithValue(ctx, "name", "Thiti")

		Get(ctx2, "id")

		go func() {
			Get(ctx2, "name")
		}()
	}()

	time.Sleep(1 * time.Second)
}

func Get(ctx context.Context, k string) {
	if v, ok := ctx.Value(k).(string); ok {
		fmt.Println(v)
	}
}

// Result:
// A001
// A001
// Thiti

จาก Code ด้านบนจะเห็นว่า ctx, ctx2 ถูกกําหนดค่า และใช้งานในหลายๆ Thread ครับ

ลองนําไปใช้งานกันดูนะครับ

ใน EP.10 จะเป็นเรื่องเกี่ยวกับ defer ในภาษา Go ท่านสามารถกดเข้าไปอ่านต่อกันได้ครับ

สําหรับบทความนี้ก็ขอจบไว้เพียงเท่านี้ครับ ขอบคุณครับ

Suggestion blogs

โดน Block Facebook จะทําไงให้เล่นได้

Imageตามหัวข้อเลยครับ ถ้าเราโดนBlock Facebook แต่ถ้าเรามีความจําเป็นจะต้องใช้งาน Facebook เราจะทําไง? คําตอบของคําถามนี้คือ ใช้ Tor ในการเปิดเว็บ Facebook ก่อนอื่น มาดูกันว่าเจ้า Tor มันคืออะไร

Goodbye wordpress, Hello Hugo

สวัสดีครับ กลับมาอีกครั้งหลังจากหายไปไม่ได้เขียน Blog นานมากๆ กลับมาครั้งนี้มีการเปลี่ยนแปลงหลายอย่างครับชื่อเว็บเปลี่ยนจากเดิม www.thitiblog.com เป็น thiti.dev แล้วในตอนนี้ และเปลี่ยนจากเดิมที่ใช้ Wordpress มาเป็น Hugo ก็จะมาเล่าให้ฟังว่า Hugo มันคืออะไร ดีอย่างไง ทําไมถึงหันมาใช้ HugoHugo คืออะไรHugo(static site generator) เป็นเครื่องมือสําหรับสร้างเว็บ Page ที่เป็น Static file ล้วนๆ ไม่ต้องมี Database มีเฉพาะไฟล์ html, css, javascript, image ที่จริงก็มีอีกหลายเจ้า แต่ Hugo จะมีจุดเด่นในเรื่องของความเร็ว และความยืดหยุ่นในการใช้งานHugo ดีอย่างไรเมื่อเป็นเว็บแบบ Static file ทำให้สามารถใช้ Static File Hosting ธรรมดาได้เลย ซึ่งจะมีราคาที่ถูก และบางที่ก็ฟรีเนื่องจาก ไม่ต้องใช้ Database และไม่มีการประมวลผลในส่วนของ Backend เพียงแค่โยนไฟล์ออกไปเท่านั้น ทำให้เว็บเร็วมากๆทำ SEO ได้ดียิ่งขึ้น เนื่องจากเป็นเว็บ แบบ Static fileหมดปัญหาเรื่องการโดน Hack เพราะเว็บทั้งเว็บเป็น Static fileข้อจํากัดของ Hugoไม่สามารถทําเว็บที่มีการเก็บข้อมูลได้ เนื่องจากไม่มี Backend และ Databaseจะต้องมี Hugo ในเครื่องที่ต้องการแก้ไขหรือ เพิ่ม Contentเมื่อมีการ Update content จะต้อง Built ก่อนทําให้ Content จะไม่ถูก Update ในทันทีสรุป Hugo เหมาะกับการสร้างเว็บแบบไหนHugo เป็น Tool สําหรับสร้างเว็บที่เป็น Static website ดังนั้นเว็บที่เหมาะจะเป็นเว็บประเภทเน้นให้บริการ Content เป็นหลัก เช่น Web blog, เว็บแนะนําสินค้า, โรงเรียน ส่วนใหญ่เว็บพวกนี้จะแสดงผลเพียงเนื้อหาอย่างเดียว อาจจะมีการ embedded forms ต่างๆ ได้ เช่น Facebook comment, Form ส่งเมล์ติดต่อสอบถาม, google analytic ฯลฯ จะง่ายในการใช้งาน และจัดการเป็นอย่างมากหลังจากที่ได้ลองเล่น ก็พบว่าเหมาะกับ Web blog ของผมเป็นอย่างมาก เพราะง่ายต่อการจัดการ ระบบไม่ซับซ้อนเกินความจําเป็นสําหรับเว็บที่เน้นนําเสนอข้อมูลเพียงอย่างเดียว และที่สําคัญเป็นมิดรกับ SEO ส่วนในเรื่อง Performance ก็ดีมากเพราะเป็น Static website เราสามารถนําเว็บของเราไปวางที่ Host ไหนก็ได้ ราคาถูก หรือไม่ก็ฟรี อย่างเช่น Google firebase hosting, netlify ฯลฯ ตอนนี้ผมจึงย้ายมาใช้งาน Hugo เป็นที่เรียบร้อย :) ถ้าท่านทําลังมองหาหรือ จะทํา Website ประเภทนี้อยู่ ก็เป็นตัวเลือกที่ดีเลยครับ

Review ซิมเพนกวิน

ซิมเพนกวิน เป็นค่ายมือถือน้องใหม่เปิดให้บริการโดยบริษัท เดอะ ไวท์สเปซ รูปแบบการให้บริการเป็นแบบระบบเติมเงิน ถ้าไม่ได้สมัครโปรโมชั่นอะไรเลยจะคิดค่าบริการตามจริงในอัตรา ค่าโทรเริ่มต้นวินาทีละ 1 สตางค์ทุกเครือข่าย และค่าเน็ตเล่นไม่เกินวันละ 20 บาท


Copyright © 2019 - 2025 thiti.dev |  v1.59.0 |  Privacy policy | 

Build with ❤️ and Astro.

Github profile   Linkedin profile   Instagram   X profile   Nostr   Youtube channel   Telegram   Email contact   วงแหวนเว็บ