Menu
CoddyTech

Ô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.

challenge icon

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 Task có các trường ID (int) và Value (int), đại diện cho công việc cần thực hiện.

    Tạo một struct Result có các trường TaskID (int) và Computed (int), đại diện cho công việc đã hoàn tất.

    Triển khai hàm Worker nhậ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ỏ đến sync.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 RunPool nhậ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ề

  • 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 RunPool vớ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: 49

Cá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

Tự luyện tập: Trình biên dịch Go online