Go routines
Perm Chao
Posted on February 7, 2021
Go routines
ลองนึกดูว่าถ้ามีคนอยู่หลายคน หนึ่งคนมีค้อนกับตะปูเพื่อจะตรอกเข้ากับกำแพง แต่ละคนมีจำนวนตะปูและพื้นที่บนกำแพงที่แตกต่างกัน แต่ว่ามีค้อนอยู่อันเดียว คนที่มีตะปูที่น้อยกว่าจริงๆต้องทำงานเสร็จก่อน แต่ก็ต้องแบ่งค้อนให้คนอื่นใช้งาน นี้คือหลักการของ Goroutine
การใช้งาน Goroutine ในภาษา Go อนุญาติให้ทำงานหลายๆงานในเวลาเดียวกัน (coroutine) งานเหล่าๆนั้น (routine) สามารถทำงานได้ใน process เดียวกัน Goroutine นั้นไม่แบ่งพื้นที่กันกับคนอื่นทำให้มันแตกต่างจาก thread แต่อย่างไรก็ตาม เราสามารถส่งตัวแปรเข้าไปอีก coroutine ได้ในโค็ด แต่อาจจะทำให้มันเกิดการทำงานที่คาดเดาไม่ได้
ลองมาดูการทำงานของ ฟังก์ชัน hello
func hello() {
fmt.Println("hello world")
}
ทีนี้เราจะลองเรียกใช้ฟังก์ชันนี้ในแบบ coroutine
func main() {
fmt.Println("hello")
// รูปแบบการเรียกใช้ฟังก์ชัน hello แบบ coroutine
go hello()
fmt.Println("end")
}
จะเห็นผลลัพท์คือไม่มีการแสดงข้อความ hello world ออกมา ว่าฟังก์ชัน main ไม่สนว่า ฟังก์ชัน hello นั้นจะทำงานนานแค่ไหน พอมันเรียกใช้งานฟังก์ชัน hello แล้ว ฟังก์ชัน main ก็ทำงานต่อเลย
มาลองใช้ Concurrent Routines
ลองจิตนาการว่าเราจะคำนวนค่าต่างๆ 2 ครั้ง ครั้งแรกเราจะหาผลรวมตัวเลขจาก 1 - 10 หลังจากนั้น หาผลรวมตัวเลขของ 1 - 100 เพื่อไม่ให้เป็นการเสียเวลาเราจะทำการคำนวนของค่านี้ไปพร้อมๆกันด้วย coroutine
func main() {
var s1 int
go func() {
s1 = sum(1, 100)
}()
log.Println(s1)
}
func sum(from, to int) int {
res := 0
for i := from; i <= to; i++ {
res += i
}
return res
}
ตัวแปร s1 จะแสดงมาเป็น 0 เนื่องจากในตอนเรียกฟังก์ชัน sum ใน main ยังทำงานไม่เสร็จและตัวแปร s1 ยังคงเป็น 0
ทีนี้ลองเรียกเป็นแบบ
func main() {
var s1 int
go func() {
s1 = sum(1, 100)
}()
// ลองเพิ่ม statement ไปเพื่อรอให้ฟังก์ชัน sum ทำงานเสร็จ
time.Sleep(time.Second)
log.Println(s1)
}
func sum(from, to int) int {
res := 0
for i := from; i <= to; i++ {
res += i
}
return res
}
WaitGroup
หากเราทำงาน coroutine มากกว่า 1 ตัวขึ้นไป เพื่อที่จะควบคุมการทำงานของ coroutine เป็นไปตามลำดับที่เราต้องการ เราต้องใช้ WaitGroup{} ช่วย
package main
import "sync"
func main() {
// สร้าง pointer ชี้ไปที่ wait group
wg := &sync.WaitGroup{}
// เพิ่มคำสั่ง asynchronous เข้าไป เพื่อบอกให้รู้ว่าให้รอ goroutine 1 ตัวด้านล่างทำงานให้เสร็จก่อนไปทำงานอื่น
wg.Add(1)
go func() {
// wait routine
...
.
.
wg.Done() // บอกตัว wait group ว่าใน go routine นี้ทำงานเสร็จแล้ว
}()
wg.Wait()
// หลังจากทำงาน go routine ด้านบนเสร็จแล้วก็ทำอันด้านล่างต่อ
………….
………….
}
หลังจากที่เราได้ปรับใช้ wait group กับการคำนวน ผลรวมตั้งแต่ 1 - 10 และ 1 - 100
package main
import (
"log"
"sync"
)
func main() {
s1 := 0
s2 := 0
wg := &sync.WaitGroup{}
// ถ้าเราใส่แค่ 1 จะเกิด panic ทันทีเพราะ wg ถูกใช้งาน 2 ครั้ง
wg.Add(2)
go sum(1, 100, wg, &s1)
go sum(1, 200, wg, &s2)
wg.Wait()
log.Println(s1, s2)
}
func sum(from, to int, wg *sync.WaitGroup, res *int) {
*res = 0
for i := from; i <= to; i++ {
*res += i
}
wg.Done()
}
... WIP ...
Posted on February 7, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.