Semaphore (tín hiệu) là cơ chế đồng bộ hóa tiến trình/luồng (thread) kinh điển. Thư viện POSIX semaphore (semaphore.h) cung cấp semaphore không tên (dùng cho thread trong cùng tiến trình) và semaphore có tên (dùng cho IPC - giao tiếp giữa các tiến trình).
Các hàm chính
| Hàm | Mô tả |
|---|---|
sem_init(sem_t *sem, int pshared, unsigned int value) | Khởi tạo semaphore không tên. pshared=0 cho thread, pshared!=0 cho tiến trình. |
sem_wait(sem_t *sem) | Giảm semaphore (khóa/chờ). Nếu giá trị = 0 thì block cho đến khi > 0. |
sem_post(sem_t *sem) | Tăng semaphore (mở khóa). Đánh thức một thread đang chờ (nếu có). |
sem_destroy(sem_t *sem) | Hủy semaphore, giải phóng tài nguyên. |
sem_open / sem_close / sem_unlink | Dành cho semaphore có tên (IPC). |
Biên dịch
Cần link thư viện pthread và rt:
gcc ten_file.c -lpthread -lrt -o ten_chuong_trinh
Ví dụ: Đồng bộ hóa 2 thread truy cập Critical Section
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t mutex; // Semaphore toàn cục
void* thread_func(void* arg) {
sem_wait(&mutex); // Vào Critical Section (khóa)
printf("\nThread %lu: Dang trong Critical Section...\n", (unsigned long)pthread_self());
sleep(3); // Giả lập công việc
printf("Thread %lu: Hoan thanh, roi Critical Section.\n", (unsigned long)pthread_self());
sem_post(&mutex); // Ra khỏi Critical Section (mở khóa)
return NULL;
}
int main() {
sem_init(&mutex, 0, 1); // Khởi tạo mutex = 1 (binary semaphore)
pthread_t th1, th2;
pthread_create(&th1, NULL, thread_func, NULL);
sleep(1); // Tạo độ trễ nhỏ để thấy rõ thứ tự
pthread_create(&th2, NULL, thread_func, NULL);
pthread_join(th1, NULL); // Đợi thread 1 kết thúc
pthread_join(th2, NULL); // Đợi thread 2 kết thúc
sem_destroy(&mutex); // Dọn dẹp
return 0;
}
Kết quả chạy
Thread 12345: Dang trong Critical Section... Thread 12345: Hoan thanh, roi Critical Section. Thread 12346: Dang trong Critical Section... Thread 12346: Hoan thanh, roi Critical Section.
Giải thích: Semaphore mutex được khởi tạo giá trị 1. Thread đầu tiên gọi sem_wait thành công (giá trị thành 0), thread thứ hai sẽ bị block tại sem_wait cho đến khi thread đầu gọi sem_post (giá trị thành 1). Điều này đảm bảo chỉ có một thread tại một thời điểm truy cập Critical Section.
Lưu ý
- Luôn kiểm tra giá trị trả về của các hàm semaphore (trả về -1 khi lỗi, đặt
errno). - Semaphore không tên chỉ dùng cho thread cùng tiến trình (hoặc tiến trình con sau fork với
pshared != 0). - Đối với IPC giữa tiến trình không liên quan, dùng
sem_openvới tên (ví dụ:/my_sem).