Ôn tập: worker pool
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 66 trên 107.
Thử thách
DễHãy xây dựng hệ thống xử lý tác vụ bằng mẫu nhóm worker! Bạn sẽ tạo một nhóm worker xử lý đồng thời các tác vụ tính toán, qua đó minh họa cách channel, goroutine và WaitGroup phối hợp để xử lý khối lượng công việc hiệu quả.
Bạn sẽ sắp xếp code trong hai tệp:
pool.go: Định nghĩa các thành phần và logic của nhóm worker.Tạo một struct
Taskcó các trườngID(int) vàValue(int), đại diện cho công việc cần thực hiện.Tạo một struct
Resultcó các trườngTaskID(int) vàComputed(int), đại diện cho công việc đã hoàn tất.Triển khai hàm
Workernhận ID worker (int), một channel chỉ nhận tác vụ (<-chan Task), một channel chỉ gửi kết quả (chan<- Result) và một con trỏ đếnsync.WaitGroup. Mỗi worker cần:- Dùng
defer wg.Done()để báo hiệu hoàn tất - Duyệt qua channel tác vụ để xử lý từng tác vụ
- Với mỗi tác vụ, tính bình phương của Value trong tác vụ đó
- Gửi một Result chứa TaskID và bình phương vừa tính
Triển khai hàm
RunPoolnhận số lượng worker (int) và một lát cắt tác vụ. Hàm này cần:- Tạo các channel có bộ đệm cho tác vụ và kết quả, dùng số lượng tác vụ làm kích thước bộ đệm
- Khởi chạy số lượng worker đã chỉ định dưới dạng goroutine
- Gửi tất cả tác vụ vào channel tác vụ, sau đó đóng channel
- Dùng một goroutine cùng với WaitGroup để đóng channel kết quả sau khi tất cả worker hoàn tất
- Thu thập tất cả kết quả vào một lát cắt rồi trả về
- Dùng
main.go: Đọc đầu vào và điều phối nhóm worker.Đọc số lượng worker, rồi đọc số lượng tác vụ. Với mỗi tác vụ, đọc ID và giá trị của tác vụ đó. Gọi
RunPoolvới số lượng worker và các tác vụ, sau đó in từng kết quả theo định dạng:Task [TaskID]: [Computed]In kết quả theo thứ tự TaskID tăng dần.
Đầu vào sẽ có dạng như sau:
- Dòng 1: Số lượng worker (số nguyên)
- Dòng 2: Số lượng tác vụ (số nguyên)
- Các dòng tiếp theo: Với mỗi tác vụ, có hai dòng: ID của tác vụ (số nguyên), rồi đến giá trị của tác vụ (số nguyên)
Ví dụ, với đầu vào:
2
4
1
3
2
5
3
2
4
7Đầu ra của bạn sẽ là:
Task 1: 9
Task 2: 25
Task 3: 4
Task 4: 49Các worker xử lý tác vụ đồng thời (bình phương của 3 là 9, bình phương của 5 là 25, v.v.), sau đó thu thập và hiển thị kết quả theo thứ tự TaskID. Với 2 worker xử lý 4 tác vụ, công việc được phân bổ hiệu quả trong nhóm.
Tự làm thử
package main
import (
"fmt"
"sort"
)
func main() {
// Đọc số lượng worker
var numWorkers int
fmt.Scanln(&numWorkers)
// Đọc số lượng tác vụ
var numTasks int
fmt.Scanln(&numTasks)
// Đọc các tác vụ
tasks := make([]Task, numTasks)
for i := 0; i < numTasks; i++ {
var id, value int
fmt.Scanln(&id)
fmt.Scanln(&value)
tasks[i] = Task{ID: id, Value: value}
}
// TODO: Gọi RunPool với các worker và tác vụ
// TODO: Sắp xếp kết quả theo TaskID tăng dần
// TODO: In từng kết quả theo định dạng: Task [TaskID]: [Computed]
}
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online