[Day #6] 7 วัน ฉันจะเขียน Golang ให้ได้!

Max Veerapat Kumchom
Grean Developers Family
3 min readAug 13, 2018

--

สวัสดีครับผมแมกซ์นะครับ หยุดสามวัน ไม่ได้ไปนั่งร้านกาแฟตามปกติเลย เพราะมันปิด 😨เลยนั่งศึกษา Goroutines และ Channels เรียกได้ว่ามันคือหัวใจสำคัญที่ทำให้ Golang นั้นมันมีประสิทธิภาพสูง และน่าเขียนมั๊กๆ แต่จะใช้เป็น เราก็ต้องเข้าใจหลักการทำงานของมันให้เรียบร้อยก่อน ซึ่งตรงนี้ก็เป็นส่วนยากเอาเรื่องเลยของ Golang เนื้อหาต่อจากนี้ก็อาจจะไม่ถูกต้องทั้งหมด เพราะเป็นการศึกษาจากหลายๆแหล่ง ซึ่งผมก็คิดหนักเลยว่า จะเขียนออกไปแล้ว มันจะทำให้คนมาเข้าใจผิดเหมือนผมไหม เอาเป็นว่าอ่านเป็นทางเลือกอีกแนวทางละกัน อย่าเชื่อผมก่อนคุณจะได้ลองเล่นมันเอง ไปกันเลย

Goroutines ทำให้ Golang สามารถเขียนโปรแกรมแบบ Concurrency ได้ แล้วมันคืออะไรกันล่ะ บทความต่อจากนี้ จะอ้างอิงจากบทความเหล่านี้ เรียกได้ว่าอ่านแล้วแปล ตามความเข้าใจของผมเลยนั่นเอง 🤓 ต้องขอขอบคุณบทความเหล่านี้อย่างสูง

ในการเขียนโปรแกรมแบบ Single-threaded เราคงคุ้นเคยกันดี โดยเราจะมีฟังก์ชันในการทำงานหลายๆฟังก์ชัน ในการทำหน้าที่แต่ละอย่าง โดยจะนำมาต่อกันเพื่อทำงานเป็นสเต็ปๆไป วางต่อกับในบรรทัดของการเขียนโค้ด โปรแกรมจะทำงานฟังก์ชันบรรทัดนั้นจนเสร็จ แล้วจึงทำงานต่อในบรรทัดต่อๆไป 😐

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3

เพื่อให้เห็นภาพ บทความนั้นเล่าถึงตัวอย่างการทำงานของโปรแกรมขุดเหมืองแร่ โดยการขุดเหมืองแร่นั้น แบ่งการทำงานเป็น การค้นหาแร่, การขุดแร่, การถลุงแร่ (การหลอม) โดยสมมุติเหมืองและแร่นั้นเป็น ชุดของข้อมูลข้อความ (array of strings) โดยเมื่อฟังก์ชันการทำงานเสร็จในแต่ละขั้นตอน ก็จะส่งข้อความออกมา 🤗

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3

แบ่งขั้นตอนการทำงานเป็น 3 ฟังก์ชันการทำงานคือ นักสำรวจแร่ (a finder), นักขุดแร่ (a miner), นักถลุงแร่ (a smelter) โดยเราจะให้โปรแกรมทำงานในรูปแบบ single thread ให้เจ้า gopher ชื่อ Gary ทำงานตามหน้าที่ทั้งหมด 😊

การทำงานแบบนี้ตรงไปตรงมาและเข้าใจได้ง่าย โดย Gary รับหน้าที่นักสำรวจก็ค้นหาแร่ เมื่อเจอ >>> Gary เปลี่ยนหน้าที่เป็นนักขุด เมื่อขุดเสร็จ >>> Gary เปลี่ยนหน้าที่เป็นนักถลุง ทำการหลอมแร่ให้เรียบร้อย เราได้ของสุดท้ายเป็น แร่จากเหมือง

แต่ถ้าเราต้องการที่จะใช้ประโยชน์จากการทำงานแบบ multiple threads เพราะฟังก์ชันต่างๆ ของเรานั้นเป็นอิสระต่อกัน จำเป็นไหมที่นักสำรวจต้องค้นหาแร่ให้ครบทั้งเหมืองก่อน แล้วจึงส่งรายงานให้นักขุด คงไม่เป็นแบบนั้น เมื่อนักสำรวจเจอแหล่งแร่ แหล่งแรกก็ควรบอกให้นักขุดเริ่มทำงานได้เลย กรณีเดียวกันนักขุด จะต้องขุดแร่ออกมาทั้งหมดก่อน แล้วจึงขนส่งไปให้ นักถลุงแร่หรือไม่ คงรอกันนาน นักถลุงแร่ก็ว่างเพราะ การขุดใช้เวลามาก และนี่แหละสิ่งที่ Concurrent programming จะเข้ามาแก้ปัญหา 🤔

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3

เรามาออกแบบการทำงานให้มีประสิทธิภาพมากขึ้นโดยการใช้ multiple threads การเพิ่มคนงานเข้าไปนั่นเอง การทำงานแต่ละอย่างไม่ได้ถูกทำโดย Gary เพียงคนเดียวแล้ว มี Jane, Peter มาทำงานที่เหลือให้ โดย Gary เพียงแค่สำรวจอย่างเดียว และกระบวนการทั้งหมดนี้จะเริ่มพร้อมกัน

การออกแบบโปรแกรมแบบนี้ สิ่งที่ท้าทายและจำเป็นอย่างต่อมา คือ เมื่อหน้าที่ทุกอย่างเป็นอิสระต่อกันทั้งหมดแล้ว จะทำอย่างไร ให้ทุกๆคน ทุกแผนกทำงานร่วมกันได้ เมื่อนักสำรวจเจอแร่ ต้องไปบอก นักขุดมาขุด ขุดเสร็จต้องส่งของไปหลอม จึงต้องมีการสร้างสายโทรศัพท์ขึ้นมาให้ สองแผนกโทรหากันเพื่อสื่อสาร ระหว่างการทำงาน และสิ่งที่เข้ามาทำหน้าที่นี้ก็คือ Goroutines and Channels นั่นเอง 🤩

Goroutines นั้นอาจให้ความหมายว่ามันคือ lightweight threads เบากว่ามากๆ เพราะการสร้าง threads นั้นใช้ทรัพยากรสูง ส่วน Goroutines การสร้างนั้นง่ายแถมฉลาดด้วย เพียงแค่คุณเพิ่มคำว่า go หน้าฟังก์ชัน การเรียกการทำงานของฟังก์ชันนั้นก็จะทำงานคล้ายการแยก threads ออกไปทำงานแล้วแต่ Goroutines != Threads นะครับ เพื่อให้เข้าใจมากขึ้นตามไปเลย 😆

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3
go <ฟังก์ชัน>// Anonymous go routine
go func() {
...
fmt.Println("I'm running in my own go routine")
...
}()

จะเห็นได้จาก Output ว่าสองฟังก์ชันการทำงานนั้น ทำงานพร้อมกันควบคู่ไปด้วยกัน ข้อสังเกต คือการรันโปรแกรมทำงานในแต่ละครั้งลำดับของการเจอแร่นั้นต่างกันออกไป เพราะการทำงานของสองฟังก์ชันนั้นทำงานแบบ Goroutines แล้ว 😉

เพียงเท่านี้เราก็สามารถสร้างโปรแกรมแบบ multiple threads ได้แล้ว แต่ยังไม่จบแค่นั้น เพราะในความเป็นจริงแล้วเรามีแร่แค่ 3 ก้อน แต่ นักสำรวจทั้งสองของเรานั้นทำงานเป็นอิสระต่อกัน จึงไม่รู้ว่าแหล่งแร่นี้ถูกค้นพบไปแล้ว จึงทำให้การทำงานนั้นซ้ำซ้อน เพื่อให้สองคนนี้มีการสื่อสารกันเราจึงต้องเรียนรู้เรื่อง Channels

Channels ช่องทางการสื่อสารระหว่าง Goroutines อย่างที่พูดๆมา การทำงานของแต่ละคน แต่ละแผนก มันจะต้องมาสายโทรศัพท์ไว้โทรคุยงานกัน ส่งต่อการกัน

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3

สิ่งนี้เปรียบเสมือนท่อส่งของ ของ Goroutines จะสามารถทำการส่ง — รับ ของจาก Goroutines อื่นๆ 😊

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3
<ชื่อของ channel> := make(chan string)

Goroutines นั้นสามารถส่งและรับของใน Channel โดยการใช้ (<-) ชี้ไปยังทิศทางที่ต้องการให้ข้อมูลของเราไป 😄

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3
myFirstChannel := make(chan string)myFirstChannel <- "hello" // ส่ง
myVariable := <- myFirstChannel // รับ

จากนี้เราสามารถนำ routines และ channels มารวมกันเขียนโปรแกรมแบบ multiple threads โดยใช้ Go’s concurrency primitives ได้แล้ว 😃

https://medium.com/@trevor4e/learning-gos-concurrency-through-illustrations-8c4aff603b3

นี้ก็คือโปรแกรมที่เราได้ทำการปรับปรุงเป็นที่เรียบร้อย ตอนนี้การทำงานต่างๆทำงานอย่างเป็นอิสระต่อกันในแต่ละ Goroutines และเมื่อทำงานเสร็จจะส่งต่อของผ่านทาง Channels โดย 🤨

  • Gary นักสำรวจ เมื่อค้นพบแร่ จะทำการส่งผลสำรวจไปในท่อ oreChannel จากนั้น
  • Jane นักขุดแร่ เมื่อมีผลสำรวจเข้ามาในท่อ oreChannel ก็จะไปขุดแร่ จากนั้น ส่งแร่ที่ขุดแล้วไปในท่อ minedOreChan
  • Peter นักถลุงแร่ เมื่อมีแร่เข้ามาในท่อ minedOreChan ก็จะนำแร่นั้นไปทำการหลอม

ซึ่งการทำงานเหล่านี้ จะทำงานไปพร้อมๆกัน โดยไม่ต้องมีใครมารอใคร 😲

นั่นแหละครับ ที่ทำให้ Golang ไม่เหมือนใคร โดยสิ่งๆนี้ ถ้าเราทำความเข้าใจมันได้เรียบร้อยแล้ว เราจะสามารถนำไปประยุกต์ ใช้กับการเขียนโปรแกรมที่มีประสิทธิภาพ และ ใช้งาน Golang ได้ตามที่ออกแบบมา ขอบคุณทุกท่านที่แวะมานะครับ หวังว่าจะสนุกกับการเขียนโปรแกรมนะครับ ผมแนะนำอ่านบทความที่ผมนำมาอ้างอิงไว้ จะเข้าใจในเชิงลึกที่ผมยังไม่ได้พูดถึงอีกเยอะเลย 😍

--

--