TỔNG HỢP 100 CÂU HỎI PHỎNG VẤN GOLANG THƯỜNG GẶP

0 0 0

Người đăng: Truong Phung

Theo Viblo Asia

100 Câu hỏi thường gặp khi phỏng vấn Golang

1. Golang là gì?

Go, hay Golang, là một ngôn ngữ lập trình mã nguồn mở được phát triển bởi Google. Nó là ngôn ngữ biên dịch, kiểu tĩnh và được thiết kế để xây dựng các ứng dụng mở rộng và hiệu suất cao.

2. Các tính năng chính của Go là gì?

  • Hỗ trợ concurrency bằng cách sử dụng Goroutines.
  • Garbage collection.
  • Statically typed và dynamic behavior.
  • Cú pháp đơn giản.
  • Biên dịch nhanh.

3. Goroutines là gì?

Goroutines là các thread nhẹ được quản lý bởi runtime của Go. Chúng là các function hoặc method chạy đồng thời với các function hoặc method khác.

4. Làm thế nào để tạo một Goroutine?

Sử dụng từ khóa go trước một function:

go myFunction()

5. Channel trong Go là gì?

Channels là cách để các Goroutines giao tiếp với nhau và đồng bộ hóa việc thực thi. Chúng cho phép gửi và nhận các giá trị.

6. Làm thế nào để khai báo một channel?

ch := make(chan int)

7.Buffered channel là gì?

Buffered channel có một dung lượng xác định và cho phép gửi các giá trị cho đến khi buffer đầy. Nó không yêu cầu một receiver phải sẵn sàng để nhận.

8. Làm thế nào để đóng một channel?

Sử dụng hàm close() :

close(ch)

9. Struct trong Go là gì?

Struct là một kiểu dữ liệu do người dùng định nghĩa cho phép nhóm các trường có kiểu dữ liệu khác nhau vào một thực thể duy nhất.

10. Làm thế nào để định nghĩa một struct?

type Person struct { Name string Age int
}

11. Interface trong Go là gì?

Interface trong Go là một kiểu dữ liệu định nghĩa tập hợp các method signatures. Nó tạo tính đa hình polymorphism bằng cách cho phép đa dạng hóa việc định nghĩa các hành vi.

12. Làm thế nào để triển khai một interface?

Một kiểu dữ liệu triển khai một interface bằng cách triển khai (implement) tất cả các method của nó:

 type Animal interface { Speak() string } type Dog struct{} func (d Dog) Speak() string { return "Woof!" }

13. Từ khóa defer là gì?

defer được sử dụng để hoãn việc thực thi một hàm cho đến khi surrounding function trả về.

14. defer hoạt động như thế nào?

Các hàm defer được thực thi theo thứ tự LIFO (Last In, First Out):

defer fmt.Println("world")
fmt.Println("hello")
// Output: hello world

15. Pointer trong Go là gì?

Pointer giữ địa chỉ bộ nhớ (memory address) của một giá trị. Nó được sử dụng để truyền tham chiếu (references) thay vì sao chép giá trị (copying values).

16. Làm thế nào để khai báo một pointer?

var p *int
p = &x

17. Sự khác biệt giữa newmake là gì?

  • new cấp phát bộ nhớ nhưng không khởi tạo giá trị.
  • make cấp phát và khởi tạo bộ nhớ cho slices, maps, và channels.

18. Slice trong Go là gì?

Slice là một mảng có kích thước động cho phép làm việc linh hoạt hơn với các chuỗi phần tử (sequences of elements).

19. Làm thế nào để tạo một slice?

s := make([]int, 0)

20. Map trong Go là gì?

Map là một tập hợp các cặp key-value.

21. Làm thế nào để tạo một map?

m := make(map[string]int)

22. Câu lệnh select là gì?

select cho phép một Goroutine đợi (wait) trên nhiều hoạt động giao tiếp (communication operations).

23. Làm thế nào để sử dụng select?

select { case msg := <-ch: fmt.Println(msg) default: fmt.Println("No message received") }

24. Channel nil là gì?

Channel nil sẽ chặn cả việc gửi và nhận.

25. Hàm init là gì?

init là một hàm đặc biệt để khởi tạo các biến cấp package. Nó được thực thi trước main.

26. Có thể có nhiều hàm init không?

Có, nhưng chúng sẽ được thực thi theo thứ tự xuất hiện.

27. Struct rỗng {} là gì?

Struct rỗng tiêu thụ (consumes) 0 byte bộ nhớ (storage).

28. Làm thế nào để xử lý lỗi trong Go?

Bằng cách trả về kiểu error và kiểm tra nó:

if err != nil { return err
}

29. Type assertion là gì?

Type assertion được sử dụng để lấy giá trị thực sự của một interface:

value, ok := x.(string)

30. Lệnh go fmt là gì?

go fmt format mã nguồn Go theo standard style.

31. Mục đích của go mod là gì?

go mod quản lý các module dependencies trong các dự án Go.

32. Làm thế nào để tạo một module?

go mod init module-name

33. Package trong Go là gì?

Package là cách nhóm các file Go liên quan lại với nhau.

34. Làm thế nào để import một package?

import "fmt"

35. Quy tắc hiển thị (visibility rules) trong Go là gì?

  • Các identifier được export bắt đầu bằng chữ cái viết hoa.
  • Các identifier không được export (private) bắt đầu bằng chữ cái viết thường.

36. Sự khác biệt giữa var:= là gì?

  • var dùng để khai báo biến với kiểu rõ ràng (explicit types).
  • := dùng để khai báo biến một cách ngắn gọn với kiểu ngầm định (inferred types).

37. panic trong Go là gì?

panic được dùng để kết thúc chương trình ngay lập tức khi gặp lỗi.

38. recover là gì?

recover được dùng để lấy lại quyền kiểm soát sau khi xảy ra panic.

39. Làm thế nào để sử dụng recover?

Nó được sử dụng trong hàm defer:

defer func() { if r := recover(); r != nil { fmt.Println("Recovered:", r) }
}()

40. Constant trong Go là gì?

Constants là các giá trị không thể thay đổi, được khai báo bằng từ khóa const.

41. Làm thế nào để khai báo một constant?

const Pi = 3.14

42. iota trong Go là gì?

iota là một lệnh tạo constant tự động tăng thêm 1.

43. go test là gì?

go test được sử dụng để chạy các unit test viết trong Go.

44. Làm thế nào để viết một hàm test?

Các hàm test phải bắt đầu bằng Test:

func TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("expected 5, got %d", result) }
}

45. Benchmarking trong Go là gì?

Benchmarking được sử dụng để đo lường hiệu suất của một hàm khi sử dụng go test.

46. Làm thế nào để viết một hàm benchmark?

Các hàm benchmark phải bắt đầu bằng Benchmark:

 func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add(2, 3) } }

47. Build constraint là gì?

Build constraints được sử dụng để bao gồm (include) hoặc loại bỏ (exclude) các file khỏi quá trình build (build process) dựa trên các điều kiện.

48. Làm thế nào để thiết lập một build constraint?

Đặt constraint trong một comment ở đầu file:

 // +build linux

49. Slices được backed bởi arrays là gì?

Slices được xây dựng trên các arrays và cung cấp một giao diện linh hoạt hơn để làm việc với các dãy số.

50. Garbage Collection trong Go là gì?

Go tự động quản lý bộ nhớ bằng cách sử dụng garbage collection, giúp giải phóng bộ nhớ không còn được sử dụng.

51. Package context trong Go là gì?

Package context được sử dụng để quản lý thời hạn (deadlines), tín hiệu hủy bỏ (cancellation signals), và các giá trị theo phạm vi yêu cầu (request-scoped values). Nó giúp kiểm soát luồng của các Goroutine và tài nguyên.

52. Làm thế nào để sử dụng context trong Go?

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

53. sync.WaitGroup là gì?

sync.WaitGroup được sử dụng để chờ một tập hợp các Goroutine hoàn thành việc thực thi.

54. Làm thế nào để sử dụngsync.WaitGroup?

 var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() // Do some work }() wg.Wait() 

55. sync.Mutex là gì??

sync.Mutex cung cấp cơ chế khóa để bảo vệ tài nguyên chia sẻ khỏi truy cập đồng thời.

56. Làm thế nào để sử dụng sync.Mutex?

var mu sync.Mutex mu.Lock() // critical section mu.Unlock()

57. select được sử dụng để làm gì với channels?

select được sử dụng để xử lý nhiều hoạt động trên channel cùng một lúc, cho phép một Goroutine chờ đợi nhiều hoạt động giao tiếp (communication operations).

58. go generate là gì??

go generate là một lệnh để tạo mã nguồn. Nó đọc các comment đặc biệt trong mã nguồn để thực thi các lệnh cần thiết.

59. Method receiver trong Go là gì?

Method receiver xác định kiểu (type) mà method được liên kết đến, có thể là bằng giá trị (value) hoặc con trỏ (pointer):

 func (p *Person) GetName() string { return p.Name }

60. Sự khác biệt giữa value receiver và pointer receiver là gì?

  • Value receiver nhận một bản sao của giá trị gốc.
  • Pointer receiver nhận một tham chiếu (reference) đến giá trị gốc, cho phép thay đổi giá trị gốc (original value).

61. Variadic function là gì?

Variadic function chấp nhận một số lượng đối số tùy ý không cố định:

 func sum(nums ...int) int { total := 0 for _, num := range nums { total += num } return total
}

62. rune trong Go là gì?

Rune là một alias cho int32 và đại diện cho một mã Unicode.

63. select block không có default case hoạt động thế nào?

Select block không có default sẽ chờ cho đến khi một trong các case của nó có thể tiến hành.

64. Ticker trong Go là gì?

A ticker gửi các sự kiện theo khoảng thời gian đều đặn:

 ticker := time.NewTicker(time.Second)

65. Làm thế nào để xử lý JSON trong Go?

Sử dụng package encoding/json để marshal và unmarshal JSON:

 jsonData, _ := json.Marshal(structure) json.Unmarshal(jsonData, &structure)

66. go vetlà gì?

go vet kiểm tra mã nguồn Go và báo cáo các lỗi tiềm ẩn, tập trung vào các vấn đề không được trình biên dịch (compiler) phát hiện.

67. Anonymous function trong Go là gì?

Anonymous function là một hàm không có tên và có thể được định nghĩa trực tiếp:

 func() { fmt.Println("Hello") }()

68. Sự khác biệt giữa ==reflect.DeepEqual() là gì?

  • == kiểm tra tính bằng nhau cho các kiểu dữ liệu nguyên thủy.
  • reflect.DeepEqual() so sánh tính bằng nhau sâu của các kiểu dữ liệu phức tạp như slices, maps, và structs.

69. time.Duration trong Go là gì?

time.Duration đại diện cho thời gian đã trôi qua giữa hai điểm và là một kiểu của int64.

70. Làm thế nào để xử lý timeout với context?

Sử dụng context.WithTimeout để đặt một thời gian chờ:

 ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel()

71. Pipeline trong Go là gì?

Pipeline là một chuỗi các giai đoạn kết nối bằng channels, trong đó mỗi giai đoạn là một tập hợp các Goroutine nhận giá trị từ upstream và gửi giá trị đến downstream.

72.Quy ước thư mục pkg trong Go là gì?

pkg là một thư mục được sử dụng để đặt các package có thể tái sử dụng. Đây là một quy ước phổ biến nhưng không bắt buộc bởi Go.

73. Làm thế nào để debug mã Go?

Sử dụng các công cụ như dlv (Delve), các câu lệnh print, hoặc package log.

74. type alias trong Go là gì?

type aliasing cho phép bạn tạo một tên mới cho một kiểu dữ liệu có sẵn

type MyInt = int

75. Sự khác biệt giữa appendcopy trong slices là gì?

  • append hêm các phần tử vào một slice và trả về một slice mới.
  • copy sao chép các phần tử từ một slice này sang một slice khác.
slice1 := []int{1, 2}
slice2 := []int{3, 4}
copy(slice2, slice1) // [1, 2]

76. Mục đích của go doc là gì?

go doc được sử dụng để hiển thị tài liệu hướng dẫn (document) cho một package, function, hoặc biến trong Go.

77. Làm thế nào để xử lý panics trong production code?

Sử dụng recover để xử lý panics một cách đúng đắn và ghi log chúng để debugging:

defer func() { if r := recover(); r != nil { log.Println("Recovered from:", r) }
}()

78. Sự khác biệt giữa mapstruct là gì?

  • map là một cấu trúc dữ liệu động với các cặp key-value.
  • struct là một cấu trúc dữ liệu tĩnh với các trường cố định.

79. Package unsafe dùng làm gì?

Package unsafe cho phép thao tác low-level memory . Không được khuyến nghị sử dụng thường xuyên.

80. Làm thế nào để thực hiện dependency injection trong Go?

Sử dụng interfaces và các hàm constructor để truyền các dependencies, giúp dễ dàng mocktest.

type HttpClient interface{} func NewService(client HttpClient) *Service { return &Service{client: client}
}

81. Goroutine khác gì với một thread?

Một Goroutine là một thread nhẹ được quản lý bởi Go runtime. Nó khác với các OS threads vì sử dụng stack khởi đầu nhỏ hơn (2KB) và được ánh xạ vào nhiều OS threads. Điều này làm cho Goroutines hiệu quả hơn trong việc xử lý concurrency.

82. Go scheduler hoạt động như thế nào?

Go scheduler sử dụng một thuật toán work-stealing với M:N scheduling, trong đó M đại diện cho OS threads và N đại diện cho Goroutines. Nó lên lịch cho các Goroutines trên các OS threads và CPUs sẵn có, nhằm cân bằng khối lượng công việc để đạt hiệu suất tối ưu.

83. Memory leak là gì và làm thế nào để ngăn chặn nó trong Go?

Memory leak xảy ra khi bộ nhớ đã cấp phát không được giải phóng. Trong Go, điều này có thể xảy ra nếu các Goroutines không được kết thúc hoặc các tham chiếu (references) đến các đối tượng được giữ lại không cần thiết. Sử dụng defer để dọn dẹp và dừng các Goroutines đúng cách để ngăn chặn memory leak.

84. Garbage collection hoạt động như thế nào trong Go?

Go sử dụng garbage collector dạng concurrent, mark-and-sweep. Nó xác định các đối tượng có thể truy cập được trong giai đoạn mark và thu thập các đối tượng không thể truy cập trong giai đoạn sweep, cho phép các Goroutines khác tiếp tục chạy trong quá trình thu gom.

85. Giải thích sự khác nhau giữa sync.Mutexsync.RWMutex.

  • sync.Mutex được sử dụng để cung cấp quyền truy cập độc quyền vào một tài nguyên được chia sẻ.
  • sync.RWMutex cho phép nhiều readers hoặc một writer tại một thời điểm, cung cấp hiệu suất tốt hơn cho các workloads nặng về đọc.

86. Race condition là gì và làm thế nào để phát hiện chúng trong Go?

Race condition xảy ra khi nhiều Goroutines truy cập một biến chia sẻ đồng thời mà không có sự đồng bộ hóa hợp lý. Sử dụng go run -race để phát hiện các race condition trong chương trình Go.

87. Struct tag là gì và được sử dụng như thế nào?

Struct tags cung cấp metadata cho các trường (fields) trong struct, thường được sử dụng cho việc serialize JSON:

type User struct { Name string `json:"name"` Age int `json:"age"`
}

88. Làm thế nào để tạo một custom error trong Go?

Tạo một custom error bằng cách implement interface error:

type MyError struct { Msg string
}
func (e *MyError) Error() string { return e.Msg
}

89. Nil pointer dereference là gì và làm thế nào để tránh nó?

Nil pointer dereference xảy ra khi bạn cố gắng truy cập giá trị mà một con trỏ nil trỏ đến. Tránh điều này bằng cách kiểm tra nil trước khi sử dụng các con trỏ.

90. Explain the difference between sync.Pool and garbage collection.

sync.Pool is used for reusing objects and reducing GC pressure. It provides a way to cache reusable objects, unlike the GC which automatically frees unused memory.

91. Làm thế nào để implement một worker pool trong Go?

Sử dụng channels để phân phối các tasks và quản lý các Goroutines worker:

jobs := make(chan int, 100)
for w := 1; w <= 3; w++ { go worker(w, jobs)
}

92. reflect trong Go là gì?

Package reflect cho phép kiểm tra runtime của các loại và giá trị. Nó được sử dụng cho các thao tác động (dynamic) như kiểm tra các trường của struct hoặc các phương thức.

93. Sự khác biệt giữa bufferedunbuffered channels là gì?

  • Một buffered channel có dung lượng, cho phép các Goroutines gửi dữ liệu mà không bị chặn (block or lock) cho đến khi buffer đầy.
  • Một unbuffered channel không có dung lượng và sẽ bị chặn cho đến khi receiver sẵn sàng.

94. Làm thế nào để tránh Goroutine leaks?

Đảm bảo các Goroutines được kết thúc bằng cách sử dụng context để hủy hoặc sử dụng timeouts với channels.

95. Sự khác nhau chính giữa panicerror là gì?

  • error được sử dụng để xử lý các tình huống dự đoán được và có thể trả về.
  • panic được sử dụng cho các tình huống không mong muốn và dừng luồng thực thi đột ngột.

96. Giải thích các interface io.Readerio.Writer.

io.Reader có phương thức Read để đọc dữ liệu, trong khi io.Writer có phương thức Write để ghi dữ liệu. Chúng tạo cơ sở cho các trừu tượng I/O của Go.

97. nil interface là gì và tại sao nó gây ra vấn đề?

nil interface là một interface có giá trị mà nó tham chiếu đến là nil. Nó có thể gây ra lỗi không mong muốn khi so sánh, vì một interface với giá trị cơ bản nil bản thân nó lại không bằng nil.

type MyInterface interface{}
var i MyInterface
var m map[string]int
i = m // This case, m is nil but i not nil

98. Làm thế nào để ngăn chặn deadlock trong các chương trình Go khi sử dụng goroutines?

Để ngăn chặn deadlock, hãy đảm bảo rằng:

  • Các Lock luôn được acquired theo cùng một thứ tự trên tất cả các Goroutines.
  • Sử dụng defer để giải phóng khóa (release locks).
  • Tránh holding lock trong khi gọi đến một hàm khác mà có tham chiếu đến cùng lock đó.
  • Hạn chế việc sử dụng channels trong vùng tranh chấp đang được khóa (locked sections).

99. Làm thế nào để tối ưu hóa hiệu suất encode/decode JSON trong Go?

  • Sử dụng các thư viện jsoniter hoặc easyjson để encode/decode nhanh hơn so với encoding/json chuẩn.
  • Định nghĩa trước các trường của struct bằng cách sử dụng json:"field_name" tags để tránh chi phí reflection.
  • Sử dụng sync.Pool để tái sử dụng các instances của json.Encoder hoặc json.Decoder khi encode/decode dữ liệu JSON lớn lặp đi lặp lại.

100. Sự khác biệt giữa GOMAXPROCSruntime.Gosched() là gì?

  • GOMAXPROCS điều khiển số lượng tối đa của các OS threads có thể thực thi Goroutines đồng thời. Nó cho phép điều chỉnh mức độ parallelism.
  • runtime.Gosched() nhường quyền xử lý cho các Goroutines khác. Nó không treo Goroutine hiện tại mà thay vào đó cho phép Go scheduler có cơ hội chạy các Goroutines khác.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Đề thi interview DevOps ở Châu Âu

Well. Chào mọi người, mình là Rice - một DevOps Engineers ở đâu đó tại Châu Âu.

0 0 87

- vừa được xem lúc

5 câu hỏi phỏng vấn Frontend giúp bạn tự tin hơn khi sử dụng bất đồng bộ trong Javascript

Một trong những điều khó khăn khi học Javascript là promises. Chúng không dễ hiểu và có thể cần một vài hướng dẫn và một thời gian kha khá để vận dụng chúng.

0 0 91

- vừa được xem lúc

Một số câu hỏi phỏng vấn liên quan đến SQL mà bạn nên biết^^

Những bài viết trước mình đã chia sẻ những kiến thức cơ bản về Database, MySQL, một số câu lệnh truy vấn cơ sở dữ liệu thường dùng mà các bạn có thể áp dụng vào công việc Tester, QA đang làm như:. MySQL cơ bản: https://link.

0 0 476

- vừa được xem lúc

Phỏng vấn tác giả Proxyman: Từ side project thành full-time business

Phỏng vấn tác giả Proxyman: Từ side project thành full-time business. Bắt đầu từ một pet product để giải quyết những vấn đề cá nhân gặp phải trong.

0 0 37

- vừa được xem lúc

[AI Interview] 12 câu hỏi phỏng vấn Deep Learning siêu hay không thể bỏ qua

Xin chào các bạn, hôm nay mình sẽ quay lại với các bạn về một chủ đề không mới những chưa bao giờ hết hot. Đó chính là các câu hỏi mà thường được hỏi khi phỏng vấn vị trí AI Engineer là gì?.

0 0 231

- vừa được xem lúc

NHỮNG CÂU TRẢ LỜI PHỎNG VẤN QC - MANUAL TESTER - FRESHER LEVEL _ DDTCMT

Em có thể mô tả life cycle của một bug. . . Nguồn hình: https://itguru.

0 0 367