0

So sánh sự khác biệt giữa Binary Semaphore và Counting Semaphore

Trong các hệ điều hành thời gian thực (RTOS), việc đồng bộ và quản lý truy cập tài nguyên dùng chung là cực kỳ quan trọng để đảm bảo tính ổn định và an toàn của hệ thống. Hai trong số các công cụ phổ biến được sử dụng để giải quyết vấn đề này là Binary SemaphoreCounting Semaphore.

Dù có tên gọi tương đồng và được dùng trong những tình huống khá giống nhau, hai loại Semaphore này thực chất phục vụ các mục đích khác nhau và có hành vi khác biệt rõ rệt.


1. Khái Niệm Cơ Bản

1.1 Binary Semaphore (Semaphore nhị phân)

Binary Semaphore chỉ có hai trạng thái: 01, tương đương với locked (đang giữ)unlocked (sẵn sàng). Semaphore loại này thường được sử dụng để:

  • Đồng bộ hóa giữa taskISR (Interrupt Service Routine)
  • Báo hiệu giữa các task (signal)
  • Bảo vệ tài nguyên chỉ có một luồng truy cập tại một thời điểm

1.2 Counting Semaphore (Semaphore đếm)

Counting Semaphore có thể giữ giá trị lớn hơn 1, cho phép nhiều task có thể "take" semaphore nếu còn chỗ trống. Nó thường được dùng để:

  • Quản lý số lượng tài nguyên hạn chế (ví dụ: 3 cổng UART)
  • Đồng bộ hóa với nhiều nguồn tín hiệu
  • Quản lý hàng đợi hoặc buffer vòng

2. Hoạt Động Của Semaphore

2.1 Sơ đồ hoạt động của Binary Semaphore

image.png

2.2 Sơ đồ hoạt động của Counting Semaphore

image.png


3. So Sánh Chi Tiết Binary vs Counting Semaphore

Tiêu chí Binary Semaphore Counting Semaphore
Giá trị tối đa 1 Tùy ý (do lập trình viên khai báo)
Mục đích chính Đồng bộ hóa Quản lý số lượng tài nguyên
Dùng để bảo vệ tài nguyên Có thể, nhưng không tối ưu như mutex Có thể, nhưng không phải mục tiêu chính
Có thể bị “give” nhiều lần Không (chỉ giữ giá trị là 1) Có thể tăng giá trị liên tục
Giao tiếp giữa Task và ISR Rất phù hợp Không phổ biến, nhưng có thể
Giải phóng bởi nhiều task Không khuyến nghị Có thể
Hành vi như tín hiệu Tốt Tốt, nếu cần đếm số lượng tín hiệu
Cấu trúc bên trong Dựa trên queue có 1 phần tử Dựa trên queue có nhiều phần tử

4. Ví Dụ Minh Họa

4.1 Ví dụ dùng Binary Semaphore để đồng bộ ISR và Task

SemaphoreHandle_t xBinarySemaphore;

void ISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void TaskWaitForISR(void *pvParameters)
{
    for (;;) {
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY)) {
            printf("Interrupt received!\n");
        }
    }
}

Diễn giải:

  • xBinarySemaphore là semaphore nhị phân, chỉ mang giá trị 0 hoặc 1.
  • ISR sẽ "give" semaphore để báo cho task biết rằng ngắt đã xảy ra.
  • Task đang xSemaphoreTake sẽ bị block cho đến khi ISR gọi xSemaphoreGiveFromISR.

4.2 Ví dụ dùng Counting Semaphore để quản lý 3 tài nguyên

SemaphoreHandle_t xCountingSemaphore;

void ResourceTask(void *pvParameters)
{
    while (1) {
        if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY)) {
            // Sử dụng tài nguyên
            printf("Task %d dùng tài nguyên\n", (int)pvParameters);
            vTaskDelay(pdMS_TO_TICKS(1000));

            // Trả lại tài nguyên
            xSemaphoreGive(xCountingSemaphore);
        }
    }
}

void main()
{
    xCountingSemaphore = xSemaphoreCreateCounting(3, 3); // 3 tài nguyên

    xTaskCreate(ResourceTask, "T1", 1000, (void *)1, 1, NULL);
    xTaskCreate(ResourceTask, "T2", 1000, (void *)2, 1, NULL);
    xTaskCreate(ResourceTask, "T3", 1000, (void *)3, 1, NULL);
    xTaskCreate(ResourceTask, "T4", 1000, (void *)4, 1, NULL);
}

Diễn giải:

  • Semaphore khởi tạo với count = 3, nghĩa là có 3 tài nguyên.
  • 4 task cạnh tranh sử dụng, chỉ 3 task chạy được cùng lúc, task còn lại phải đợi.
  • Sau khi sử dụng xong, task sẽ give lại semaphore.

5. Ưu và Nhược Điểm

5.1 Binary Semaphore

Ưu điểm:

  • Đơn giản, dễ sử dụng
  • Hiệu quả khi đồng bộ ISR với task
  • Không cần đếm hay kiểm tra giá trị

Nhược điểm:

  • Không dùng được cho nhiều tài nguyên
  • Không lưu trữ số lượng tín hiệu nếu nhiều hơn 1 ISR xảy ra trước khi task xử lý
  • Dễ bị “mất tín hiệu” nếu task chưa take mà ISR gọi nhiều lần liên tiếp

5.2 Counting Semaphore

Ưu điểm:

  • Quản lý nhiều tài nguyên rất tốt
  • Có khả năng tích lũy tín hiệu (giữ số lượng)
  • Có thể linh hoạt dùng trong nhiều tình huống đồng bộ phức tạp

Nhược điểm:

  • Phức tạp hơn binary
  • Có thể gây lỗi nếu không kiểm soát chính xác việc takegive
  • Không thích hợp nếu chỉ cần đồng bộ đơn giản hoặc giao tiếp ISR-task

6. Khi Nào Dùng Binary, Khi Nào Dùng Counting?

Trường hợp Dạng Semaphore phù hợp
ISR báo hiệu cho Task Binary Semaphore
Task cần sử dụng tài nguyên độc quyền Mutex (ưu tiên hơn binary)
Có nhiều tài nguyên giống nhau (ví dụ 5 UART) Counting Semaphore
Có nhiều tín hiệu đến liên tiếp Counting Semaphore
Cần “bật đèn xanh” cho task bắt đầu Binary Semaphore

7. Kết Luận

Binary Semaphore và Counting Semaphore đều là những công cụ mạnh mẽ cho việc đồng bộ hóa trong RTOS như FreeRTOS. Việc lựa chọn đúng loại semaphore không chỉ giúp hệ thống hoạt động ổn định mà còn tránh được các lỗi tiềm ẩn như race condition, deadlock, hoặc dropped signals.

Tổng kết:

  • Binary Semaphore: Sử dụng khi cần đồng bộ một sự kiện duy nhất giữa 2 thành phần (ví dụ ISR và Task).
  • Counting Semaphore: Dùng khi cần quản lý số lượng nhiều tài nguyên hoặc nhiều tín hiệu xảy ra liên tiếp.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí