Menu
CoddyTech

Cơ bản về goroutine

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 59 trên 107.

goroutine là một luồng thực thi nhẹ do môi trường chạy Go quản lý. Khác với các luồng truyền thống tiêu tốn nhiều bộ nhớ, goroutine bắt đầu với chỉ vài kilobyte không gian ngăn xếp, cho phép bạn chạy đồng thời hàng nghìn goroutine.

Để bắt đầu một goroutine, bạn chỉ cần đặt từ khóa go trước lời gọi hàm:

func sayHello(name string) {
    fmt.Println("Hello,", name)
}

func main() {
    go sayHello("Alice")  // chạy đồng thời
    go sayHello("Bob")    // chạy đồng thời
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Done")
}

Khi go sayHello("Alice") chạy, chương trình không chờ sayHello hoàn tất. Thay vào đó, chương trình lập tức tiếp tục sang dòng tiếp theo trong khi sayHello chạy ở chế độ nền. Vì vậy, chúng ta cần time.Sleep. Nếu không có nó, main sẽ thoát trước khi các goroutine hoàn tất.

Bạn cũng có thể khởi chạy goroutine bằng các hàm ẩn danh (anonymous function):

go func(msg string) {
    fmt.Println(msg)
}("Hello from goroutine")

Bản thân hàm main chạy trong một goroutine. Khi hàm này trả về, tất cả goroutine khác đều bị kết thúc ngay lập tức. Chúng không có cơ hội hoàn thành. Dùng time.Sleep là giải pháp tạm thời; trong các bài học tiếp theo, bạn sẽ học các kỹ thuật đồng bộ phù hợp bằng channel và wait group.

challenge icon

Thử thách

Dễ

Hãy xây dựng một trình chạy tác vụ để thực thi đồng thời nhiều tác vụ bằng goroutine. Bạn sẽ tạo một hệ thống đơn giản, trong đó các tác vụ chạy ở nền trong khi chương trình chính tiếp tục hoạt động, qua đó minh họa cách goroutine cho phép thực thi đồng thời.

Bạn sẽ sắp xếp code trong hai tệp:

  • tasks.go: Định nghĩa logic thực thi tác vụ.

    Tạo một struct Task với các trường Name (string) và Duration (int, biểu thị số mili giây).

    Cài đặt phương thức Run() cho Task để mô phỏng công việc bằng cách tạm dừng trong khoảng thời gian của tác vụ, sau đó in ra: Task [Name] completed

    Tạo hàm RunAllTasks(tasks []Task) để chạy phương thức Run() của từng tác vụ dưới dạng goroutine. Sau khi khởi chạy tất cả goroutine, hàm nên tạm dừng trong 500 mili giây để chúng hoàn tất, rồi in ra: All tasks launched

  • main.go: Đọc thông tin tác vụ và điều phối quá trình thực thi đồng thời.

    Đọc số lượng tác vụ, sau đó đọc tên và thời lượng của từng tác vụ. Tạo các tác vụ và truyền chúng cho RunAllTasks. Khi hàm này trả về, in ra: Main function done

Đầu vào sẽ có dạng sau:

  • Dòng 1: Số lượng tác vụ (số nguyên)
  • Các dòng tiếp theo: Với mỗi tác vụ, hai dòng gồm tên tác vụ, rồi thời lượng tính bằng mili giây

Ví dụ, với đầu vào:

3
Download
100
Process
50
Upload
75

Đầu ra cần hiển thị các tác vụ hoàn tất (thứ tự có thể thay đổi do thực thi đồng thời), sau đó là các thông báo hoàn tất:

Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function done

Lưu ý: Vì goroutine chạy đồng thời, các tác vụ có thời lượng ngắn hơn thường hoàn tất trước. Thứ tự hoàn tất phụ thuộc vào thời lượng của chúng: tác vụ ngắn hơn hoàn tất trước tác vụ dài hơn.

Dùng time.Sleep cùng với time.Duration và time.Millisecond để tạo khoảng dừng. Hãy nhớ rằng nếu không có lệnh tạm dừng trong RunAllTasks, hàm sẽ trả về trước khi các goroutine hoàn tất công việc.

Tự làm thử

package main

import (
	"fmt"
)

func main() {
	// Đọc số lượng tác vụ
	var numTasks int
	fmt.Scanln(&numTasks)

	// TODO: Tạo một slice để lưu các tác vụ
	
	// TODO: Đọc tên và thời lượng của từng tác vụ
	// Với mỗi tác vụ:
	//   - Đọc tên tác vụ
	//   - Đọc thời lượng (dưới dạng số nguyên)
	//   - Tạo một Task và thêm vào slice của bạn

	// TODO: Gọi RunAllTasks với slice chứa các tác vụ

	// TODO: In ra "Main function done"
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

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