ด้านล่างนี้เป็นรายการคำถามสัมภาษณ์ GoLang ที่พบบ่อยและคำตอบที่เกี่ยวข้องตามประสบการณ์ของผู้เขียน รายการนี้มีการปรับปรุงบ่อยครั้งด้วยความเข้าใจใหม่ๆ โปรดติดตาม
What is the GMP model of GoLang?
GoLang ใช้ goroutine เพื่อให้บรรลุถึง concurrency และมีชื่อเสียงในการรองรับ concurrency สูง เนื่องจากภาษาได้กำหนดระบบการ dispatching และประมวลผล goroutine ของตัวเอง ซึ่งเป็นที่รู้จักกันดีในชื่อ GMP model
วิธีการทำงานคือ M ถูกกำหนดโดยทั่วไปว่าเป็น OS thread ที่ถูกสร้างขึ้น (จากนั้นแต่ละ M thread ควรเชื่อมโยงกับ Process queue และแต่ละ Process queue สามารถมี goroutine ได้หลายตัว จำนวน Process queue ถูกกำหนดด้วย GOMAXPROCS()
เมื่อ M ประมวลผล G ทั้งหมดจาก P ที่เกี่ยวข้อง มันจะขโมย G จาก M อื่นๆ และจาก global P queue ด้วย
หาก M ค้างเนื่องจาก IO blocks หรือ network blocks มันจะ detach ตัวเองออกจาก P และ P นี้จะเชื่อมโยงกับ M อื่นที่พร้อมใช้งาน หรือหากไม่มีอยู่ M ใหม่จะถูกสร้างขึ้น
หาก goroutine ไม่สามารถใส่ลงใน local P ได้ มันจะถูกใส่ใน global P queue หาก M ไม่สามารถขโมย G จาก M อื่นๆ ได้ มันจะดึงมาจาก global P queue
แต่ละ goroutine จะมีขนาด stack เพียง 2K ซึ่งค่อนข้างเล็ก และนั่นคือเหตุผลที่ GoLang รองรับ concurrency ได้มากกว่า และการสลับระหว่างการ execution ของ goroutine นี้ขึ้นอยู่กับ user thread space ดังนั้นค่าใช้จ่ายในการสลับ context จึงไม่สูงเท่ากับ thread หรือ process
What is the GC mechanism in GoLang?
มีสามเฟส: Mark Setup, Mark และ Mark Termination Mark Setup และ Mark Terminations คือ STW เฟส Mark เป็น concurrent
เมื่อทำการ Mark Setup Write Barrier จะถูกเปิดใช้งานเพื่อให้มั่นใจในความสมบูรณ์ของข้อมูล จากนั้นจะรอให้ goroutine ที่กำลังทำงานทั้งหมดหยุด
เมื่อ Marking เริ่มต้น มันจะใช้ CPU 25% สำหรับตัวเองและมี P สำหรับตัวเองเพื่อทำการ collection เฟส marking จะ mark ส่วนหัวที่ยังใช้งานอยู่ มันพยายามดูผ่าน stack ของ goroutine ทั้งหมดและ traverse root pointer จากนั้น traverse จาก root และทำการ mark ในกรณีที่ mark ไม่สามารถทำเสร็จทันเวลา มันจะทำ Mark Assist ซึ่งหมายถึงขอให้ application goroutine อื่นๆ ช่วย เพื่อเร่ง mark ให้เร็วขึ้น
เมื่อทำการ Mark Termination Write Barrier จะถูกปิดใช้งานและการ clean up จะเสร็จสิ้น
When to start to do GC?
- GC Percentage โดยที่เปอร์เซ็นต์ของหน่วยความจำใหม่ที่จะถูกจัดสรรเมื่อเทียบกับหน่วยความจำที่ใช้งานก่อนหน้าในระหว่างรอบ GC ก่อนหน้า
- Manual trigger โดยการเรียก runtime.GC()
How does a Slice work underlying?
Slice อธิบายถึงส่วนหนึ่งของ array และไม่เหมือนกับ array
Slice header มี length, capacity และ zeroth element pointer ภายใต้มันได้รับการสนับสนุนโดย array หาก capacity ถึงแล้ว จะไม่สามารถเติบโตได้อีกต่อไป ต้องสร้าง array ใหม่และ reallocate หากไม่ถึง capacity จะสามารถเติบโตได้เมื่อจำเป็น
Panic, defer and recover
For defer how it works FILO
-
อาร์กิวเมนต์ของฟังก์ชัน deferred จะถูกประเมินเมื่อ statement defer ถูกประเมิน
-
การเรียกฟังก์ชัน Deferred จะถูก execute ในลำดับ Last In First Out หลังจากฟังก์ชันโดยรอบ return
-
ฟังก์ชัน Deferred อาจอ่านและกำหนดค่าให้กับ named return values ของฟังก์ชันที่ return
Panic จะ return การ execution และ defers ในฟังก์ชันจะดำเนินต่อไป
Recover มีประโยชน์เฉพาะภายในฟังก์ชัน deferred ในฟังก์ชันปกติ recover ไม่มีผลและไม่ทำอะไรเลย
Defer recover ทำงานสำหรับ goroutine ปัจจุบันเท่านั้น มันจะไม่ทำงานสำหรับ goroutine ที่ถูก spawn
Context, it’s powerful
4 ฟังก์ชันใน Context interface:
- Deadline(),
- Done(),
- Err(),
- Value(key interface{})
ประโยชน์สำหรับสิ่งนี้คือการ sharing resource, การควบคุม goroutine
มี goroutine ที่ได้มาเช่น WithCancel, WithDeadline, WithTimeout, WithValue
WithCancel:
-
ฟังก์ชัน Cancel จะถูก cascaded ไปยัง sub-contexts
-
เมื่อ sub-context ใหม่ถูกสร้างขึ้น มันจะถูก registered กับ parent context
WithValue
-
key ต้อง comparable เนื่องจากมีการตรวจสอบ c.key == key เมื่อเรียกฟังก์ชัน Value()
-
slice, map และ function ไม่ comparable ส่วนใหญ่เป็นเพราะ slice ไม่ comparable เนื่องจากเป็น reference type ที่การเปรียบเทียบ address เพียงอย่างเดียวไม่มีประโยชน์ และความซับซ้อนเพิ่มขึ้นสำหรับการเปรียบเทียบ contents ใน slice เนื่องจากคุณสมบัติ len/cap
Channel
Channel เป็นวิธีในการ share memory โดยการสื่อสาร
มี channel แบบ buffered และ non-buffered
- Close nil หรือ closed channel จะ panic
- Send value ไปยัง nil channel จะ block ตลอดไป closed channel จะ panic
- Receive data จาก nil channel จะ block ตลอดไป closed channel จะ return ทันที
When would goroutine leak happen?
สถานการณ์บางอย่างรวมถึง:
- เมื่อ channel ไม่ถูก closed ในขณะที่ goroutine ยังคงรอรับข้อมูลจาก channel
- เมื่อ resource บางอย่างไม่ถูก closed อย่างถูกต้องในขณะที่ goroutine บางตัวยังคงรอการตอบสนองจากอีกฝั่งและไม่มีการตั้งค่า timeout ที่เพียงพอสำหรับการ blocking IO
What are the differences between new() and make()?
new()
จะ allocate memory เท่านั้น ในขณะที่ make()
จะ initialize ข้อมูล
Object ที่สร้างด้วย new()
พร้อมใช้งาน แต่ต้อง initialize properties อย่างชัดเจนก่อนจึงจะสามารถใช้งานได้เมื่อจำเป็น
make()
สามารถใช้ได้เฉพาะกับ slice, map และ channel types เท่านั้น
The detailed difference can be found here.
Differences between pointer and non-pointer method receiver?
มันก็เหมือนกับ pass by reference vs pass by value ในการเรียกฟังก์ชัน
หาก struct จะถูก mutated หรือ struct มีขนาดใหญ่เกินไปสำหรับ receiver เราจะเลือกกำหนด pointer method receiver
หากบางฟังก์ชันมี pointer receivers ฟังก์ชันที่เหลือก็ควรมีด้วยเพื่อให้สอดคล้องกัน
How to check whether a struct implements an interface?
ไม่มี keyword implements ใน GoLang เหมือนกับภาษาอื่นๆ เช่น Java หาก struct implements ทุก method ของ interface จะถือว่า implements interface หาก struct code ไม่อยู่ในที่เดียวกับ interface definition อาจเป็นเรื่องยากที่จะทราบว่า struct implements interface เฉพาะหรือไม่ สิ่งนี้อาจทำให้เกิดปัญหา runtime ในบางกรณี
มีสองสามวิธีในการตรวจสอบ:
- Type assertion กับ dummy object
var _ = (SomeStruct{}).(SomeInterface)
- Using reflection reflect.TypeOf(OneInterface).Implements(reflect.TypeOf(SomeInterface).Elem())
For other methods, can find here.