Giới Thiệu Về Lập Trình Concurrent Trong Golang
Concurrency (đồng thời) là một trong những yếu tố cốt lõi trong lập trình ngày nay, đặc biệt trong việc phát triển các ứng dụng hiệu suất cao. Golang, được phát triển bởi Google, nổi bật với khả năng xử lý đồng thời một cách hiệu quả. Trong bài viết này, chúng ta sẽ đi sâu vào lập trình concurrency trong Golang và tìm hiểu các khái niệm cơ bản cùng các phương pháp để xây dựng các ứng dụng mạnh mẽ.
Concurrency Là Gì?
Concurrency là khả năng thực hiện nhiều tác vụ cùng một lúc bằng cách chia nhỏ chúng thành các tác vụ nhỏ hơn. Dù rằng mọi tác vụ không phải lúc nào cũng chạy đồng thời, khả năng chuyển đổi nhanh giữa các tác vụ giúp cải thiện hiệu suất của ứng dụng. Trong Golang, mỗi tác vụ được coi là một Goroutine.
Goroutines Là Gì?
Goroutines là các luồng thực thi nhẹ (lightweight execution threads) mà Golang cung cấp. Chúng được quản lý bởi Go runtime, giúp tiết kiệm tài nguyên bộ nhớ và cho phép thực hiện hàng nghìn tác vụ đồng thời mà không gây ra sự phức tạp.
- Mỗi ứng dụng Golang có một Goroutine chính, được gọi là main Goroutine. Nếu main Goroutine dừng, tất cả Goroutines khác cũng sẽ ngừng hoạt động.
Để khởi tạo một Goroutine, bạn chỉ cần thêm từ khóa go
trước lời gọi hàm:
func quan() {
// code
}
go quan()
Ví Dụ Cơ Bản Về Goroutines
Hãy xem một ví dụ đơn giản sau:
package main
import (
"fmt"
)
func showName(s string) {
for i := 0; i < 3; i++ {
fmt.Println(s)
}
}
func main() {
go showName("Quân")
go showName("Troy")
}
Khi chạy chương trình trên, bạn có thể thấy rằng không có đầu ra nào được hiển thị. Điều này xảy ra do main Goroutine kết thúc trước khi hai Goroutines kia hoàn thành nhiệm vụ.
Sử Dụng time.Sleep
Để Đợi Các Goroutines
Để đảm bảo rằng tất cả các Goroutines hoàn thành trước khi kết thúc ứng dụng, bạn có thể sử dụng time.Sleep
để tạm dừng chương trình:
package main
import (
"fmt"
"time"
)
func showName(s string) {
for i := 0; i < 3; i++ {
fmt.Println(s)
}
}
func main() {
go showName("Quân")
go showName("Troy")
time.Sleep(1 * time.Second)
}
Kết quả sẽ như mong đợi:
Troy
Troy
Troy
Quân
Quân
Quân
Quản Lý Goroutines Với WaitGroup
Thay vì dùng time.Sleep
, chúng ta có thể dùng WaitGroup
để quản lý khi nào Goroutines hoàn thành:
package main
import (
"fmt"
"sync"
)
func sayName(s string, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 3; i++ {
fmt.Println(s)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go sayName("Quân", &wg)
go sayName("Troy", &wg)
wg.Wait()
fmt.Println("Ứng dụng kết thúc")
}
Tìm Hiểu Về Channel
Trong Golang, Channel cho phép Goroutines giao tiếp với nhau. Bạn có thể tạo Channel bằng cách sử dụng hàm make
:
ch := make(chan string)
Ví dụ sau cho thấy cách gửi và nhận dữ liệu qua Channel:
package main
import "fmt"
func sayName(s string, ch chan string) {
result := ""
for i := 0; i < 3; i++ {
result += s + " là tên của tôi, "
}
ch <- result
}
func main() {
ch := make(chan string)
go sayName("Quân", ch)
fmt.Println(<-ch)
fmt.Println("Ứng dụng kết thúc")
}
Hiểu Về Deadlock
Deadlock là tình huống mà một hoặc nhiều Goroutines đang chờ mà không bao giờ kết thúc. Hãy xem ví dụ sau:
func main() {
ch := make(chan int)
ch <- 1 // Dòng này sẽ gây ra deadlock
}
Khi chương trình không có goroutine nào khác nhận dữ liệu từ Channel, tình huống Deadlock sẽ xảy ra.
Buffered Channel
Buffered Channel cho phép chứa một số giá trị nhất định mà không cần phải nhận ngay lập tức. Bạn có thể tạo một Buffered Channel như sau:
ch := make(chan int, 3)
Khi bạn gửi giá trị vào channel, nếu không vướt quá kích thước của buffer, main Goroutine sẽ không bị chặn.
Kết Luận
Bài viết đã giới thiệu về lập trình concurrency trong Golang và giải thích các khái niệm cơ bản như Goroutines, WaitGroup, và Channel. Các ưu điểm của việc sử dụng concurrency giúp xây dựng các ứng dụng mạnh mẽ và linh hoạt, tối ưu hóa tài nguyên sẵn có. Hãy tiếp tục khám phá và thực hành với Goroutines để nâng cao kỹ năng lập trình của bạn trong Golang. Nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại để lại ý kiến bên dưới nhé!