POSIX 信号量(Semaphores)是一种同步机制,用于控制对共享资源的访问。POSIX 信号量有两种类型:二进制信号量(Binary Semaphores)和计数信号量(Counting Semaphores)。它们提供了原子的信号量操作,可以用于实现互斥和同步。下面详细介绍 POSIX 信号量的概念、用途、API 以及示例代码。
概述
POSIX 信号量可以分为两类:
- 有名信号量 (Named Semaphores):可以在多个进程之间共享。
- 无名信号量 (Unnamed Semaphores):仅限于在同一进程内的线程之间共享。
特点
- 原子操作:信号量操作是原子的,可以防止并发访问导致的问题。
- 互斥:二进制信号量可以用于互斥访问共享资源。
- 同步:计数信号量可以用于线程之间的同步。
- 跨进程:有名信号量可以在不同的进程之间进行同步。
API
POSIX 信号量的主要 API 如下:
有名信号量
-
sem_open():
sem_t *sem_open(const char *name, int oflag[, ...])
: 创建或打开一个有名信号量。- 参数
name
是信号量的名字,oflag
指定创建方式,如O_CREAT
表示创建新的信号量,后面可以跟value
来设置初始值。
-
sem_close():
int sem_close(sem_t *sem)
: 关闭一个有名信号量。- 参数
sem
是指向信号量的指针。
-
sem_unlink():
int sem_unlink(const char *name)
: 删除一个有名信号量。- 参数
name
是信号量的名字。
无名信号量
-
sem_init():
int sem_init(sem_t *sem, int pshared, unsigned int value)
: 初始化一个无名信号量。- 参数
sem
是指向信号量的指针,pshared
指定信号量是否可以在不同线程间共享(非零值表示可以),value
是信号量的初始值。
-
sem_destroy():
int sem_destroy(sem_t *sem)
: 销毁一个无名信号量。- 参数
sem
是指向信号量的指针。
共同操作
-
sem_wait():
int sem_wait(sem_t *sem)
: 等待信号量。- 参数
sem
是指向信号量的指针。
-
sem_trywait():
int sem_trywait(sem_t *sem)
: 尝试等待信号量,如果信号量不可用则立即返回。- 参数
sem
是指向信号量的指针。
-
sem_post():
int sem_post(sem_t *sem)
: 释放信号量。- 参数
sem
是指向信号量的指针。
-
sem_getvalue():
int sem_getvalue(const sem_t *sem, int *sval)
: 获取信号量的值。- 参数
sem
是指向信号量的指针,sval
是指向整数的指针,用于返回信号量的值。
示例代码
下面是一个简单的示例,展示了如何使用无名信号量来同步多个线程的操作:
1#include <semaphore.h>
2#include <pthread.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6
7// 定义信号量
8sem_t semaphore;
9
10// 线程函数
11void *thread_function(void *arg) {
12 int thread_id = *(int *)arg;
13
14 // 等待信号量
15 sem_wait(&semaphore);
16 printf("Thread %d acquired the semaphore\n", thread_id);
17
18 // 模拟一些操作
19 sleep(1);
20
21 // 释放信号量
22 sem_post(&semaphore);
23 printf("Thread %d released the semaphore\n", thread_id);
24
25 return NULL;
26}
27
28int main() {
29 int i;
30 pthread_t threads[5];
31
32 // 初始化信号量
33 if (sem_init(&semaphore, 0, 1) == -1) {
34 perror("sem_init");
35 exit(EXIT_FAILURE);
36 }
37
38 // 创建线程
39 for (i = 0; i < 5; i++) {
40 int *thread_id = (int *)malloc(sizeof(int));
41 *thread_id = i + 1;
42 if (pthread_create(&threads[i], NULL, thread_function, (void *)thread_id) != 0) {
43 perror("pthread_create");
44 exit(EXIT_FAILURE);
45 }
46 }
47
48 // 等待线程结束
49 for (i = 0; i < 5; i++) {
50 if (pthread_join(threads[i], NULL) != 0) {
51 perror("pthread_join");
52 exit(EXIT_FAILURE);
53 }
54 }
55
56 // 销毁信号量
57 if (sem_destroy(&semaphore) == -1) {
58 perror("sem_destroy");
59 exit(EXIT_FAILURE);
60 }
61
62 return 0;
63}
注意事项
- 初始化和销毁:确保在使用信号量之前正确初始化它,并在不再需要时销毁它。
- 死锁避免:避免在锁定多个信号量时发生死锁。一个常见的做法是始终以相同的顺序锁定信号量。
- 超时等待:POSIX 信号量没有直接提供超时等待的功能,但可以通过结合使用
sem_trywait()
和usleep()
等函数实现类似的效果。 - 线程取消:在使用信号量时,确保处理线程取消的情况,避免资源泄漏。
POSIX 信号量是多线程编程中非常重要的工具,它们可以帮助我们有效地解决并发问题。理解和熟练掌握这些概念对于编写高效、可靠的多线程应用程序至关重要。