goroutine
Đơn vị Concurency của ngôn ngữ Go được gọi là goroutine và sử dụng từ khóa go
để bắt đầu một goroutine.
Từ khóa go
phải được theo sau bởi hàm, có thể là hàm được đặt tên hoặc hàm không tên và giá trị trả về của hàm sẽ bị bỏ qua.
Việc thực thi go
không bị chặn (non-blocking).
Trước tiên hãy xem một ví dụ:
Xét từ kết quả thực thi, giá trị của dãy Fibonacci được tính toán thành công, cho biết chương trình không bị chặn tại spinner
, và hàm spinner
tiếp tục in các ký tự trên màn hình, cho biết chương trình đang được thực thi.
Khi giá trị của dãy Fibonacci được tính toán xong, hàm main
sẽ in kết quả và thoát, đồng thời spinner cũng thoát.
Hãy xem một ví dụ khác, thực hiện vòng lặp 10 lần và in tổng của hai số:
Có một vấn đề, không có gì trên màn hình, tại sao?
Điều này phụ thuộc vào cơ chế thực thi của chương trình Go. Khi một chương trình bắt đầu, chỉ có một goroutine duy nhất gọi hàm main
, được gọi là main goroutine. Các goroutine mới được tạo với từ khóa go
và được thực thi đồng thời. Khi hàm main
trở lại, nó không đợi các goroutine khác thực thi xong mà kết thúc tất cả các goroutine một cách cưỡng chế.
Có cách nào để giải quyết nó? Tất nhiên là có, xin vui lòng đọc dưới đây.
channel
Nói chung, khi viết các chương trình đa luồng (multi threading), bạn sẽ gặp một vấn đề: giao tiếp giữa các thread. Các phương thức giao tiếp phổ biến bao gồm tín hiệu, bộ nhớ dùng chung, v.v. Cơ chế giao tiếp giữa các goroutine là channel.
Tại channel bằng hàm make
:
Channel hỗ trợ 3 hoạt động chính:send
,receive
và close
.
unbuffered channel
Hàm make
chấp nhận hai tham số, tham số thứ hai là tham số tùy chọn, cho biết dung lượng channel. Bỏ qua hoặc đặt bằng 0 để tạo unbuffered channel.
Thao tác gửi trên unbuffered channel sẽ bị chặn cho đến khi một goroutine khác thực hiện thao tác nhận trên channel tương ứng. Ngược lại, nếu việc nhận được thực hiện trước, thì goroutine nhận sẽ chặn cho đến khi một con goroutine khác thực hiện việc gửi trên kênh tương ứng.
Vì vậy, một unbuffered channel là một channel đồng bộ.
Hãy sử dụng một unbuffered channel để giải quyết các vấn đề trong ví dụ trên.
Kết quả có thể được xuất ra bình thường.
main
goroutine sẽ bị block, cho đến khi nó đọc giá trị từ channel, chương trình tiếp tục thực thi và cuối cùng thoát.
buffered channel
Tạo một channel với bộ đệm với dung lượng 5:
Thao tác gửi trên buffered channel sẽ chèn một phần tử vào cuối channel và thao tác nhận sẽ loại bỏ một phần tử khỏi phần đầu của channel. Nếu channel đầy, việc gửi sẽ bị block cho đến khi một goroutine khác thực hiện việc nhận. Ngược lại, nếu channel trống, việc nhận sẽ chặn cho đến khi một goroutine khác thực hiện gửi.
Bạn có cảm thấy rằng, trên thực tế, các buffered channel giống như các hàng đợi (queue).
Unidirection channel (channel một chiều)
Kiểu chan<- int
là channel chỉ có thể gửi và kiểu <-chan int
là channel chỉ có thể nhận.
Bất kỳ channel hai chiều nào cũng có thể được sử dụng làm channel một chiều.
Một điều cần lưu ý nữa là close
chỉ có thể được sử dụng trên channel gửi và sẽ báo lỗi nếu nó được sử dụng trên channel nhận.
Xem ví dụ về channel một chiều:
sync
Package sync
cung cấp hai kiểu: sync.Mutex
và sync.RWMutex
, kiểu thứ nhất là một mutex và kiểu thứ hai là read-write mutex.
Khi một goroutine lấy được Mutex
, những goroutine khác chỉ có thể đợi cho đến khi mutex được giải phóng bất kể đọc hay viết.
RWMutex
thuộc mô hình một lần ghi nhiều lần đọc cổ điển, khi mutex đọc bị chiếm dụng, nó sẽ ngăn ghi nhưng không ngăn đọc. Mutex ghi ngăn chặn cả viết và đọc.