目录
1: 信号量的基本概念
-
背景介绍:
- 信号量是一种并发编程中的同步原语,它用于协调多个进程对共享资源的访问。
- 信号量可以控制资源的访问,例如,在多个进程尝试写入同一文件时,使用信号量可以确保只有一个进程能够在某一时刻执行写操作。
-
命名信号量(Named Semaphore)的工作方式:
- 命名信号量是共享在多个进程之间的信号量,可以通过名称来访问。
- 要使用信号量,首先需要包含
<semaphore.h>
头文件。
-
创建命名信号量:
- 使用
sem_open
函数创建或打开信号量。例如,sem_open("/semaphore_name", O_CREAT, 0666, initial_value)
。 - 参数解释:
"/semaphore_name"
:信号量的名称,需要唯一。O_CREAT
:表示如果信号量不存在则创建它。0666
:权限标识(可读写)。initial_value
:信号量的初始值,表示可用的资源数。
- 使用
-
获取和释放信号量:
sem_wait
:获取信号量。如果信号量值为0,则进程会阻塞,直到其他进程释放信号量。sem_post
:释放信号量,使信号量值加1,允许其他阻塞的进程继续执行。
-
关闭和删除信号量:
sem_close
:关闭信号量,表示不再需要使用它。sem_unlink
:删除信号量,使其不再在系统中存在。
2: 命名信号量的示例代码
sem_t *sema;
sema = sem_open(name, O_CREAT, 0666, VALUE);
sem_wait(sema); // 获取信号量
printf("减少信号量\n");
sem_post(sema); // 释放信号量
printf("增加信号量\n");
sem_close(sema); // 关闭信号量
sem_unlink(name); // 删除信号量
-
- 代码解释:
sem_open
:创建一个新的命名信号量。sem_wait
:等待并获取信号量(进入临界区)。sem_post
:释放信号量(离开临界区)。sem_close
和sem_unlink
:分别用于关闭和删除信号量。
- 编译:
-
gcc semaphore_example.c -pthread -o semaphore_example
-
- 代码解释:
3. 无名信号量(Unnamed Semaphore)
使用方法,重点是如何在线程中使用无名信号量来控制对共享资源的访问。
背景(Background)
无名信号量与具名信号量的区别在于,具名信号量通过sem_open
创建,有一个名字可以在不同的进程间共享。无名信号量则存在于内存中,仅在同一进程内的多个线程或已映射相同共享内存的进程之间共享。
-
无名信号量的创建:我们需要通过
sem_init()
函数来初始化无名信号量,而不是使用sem_open
。 -
sem_init() 函数定义如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
-
sem
:指向信号量的指针。pshared
:如果为0,信号量只在线程间共享;如果为非零,信号量可以在进程间共享。value
:信号量的初始值。
-
使用信号量:使用
sem_wait()
来等待信号量,sem_post()
来释放信号量。 -
销毁信号量:通过
sem_destroy()
函数销毁无名信号量。销毁后,不能再使用此信号量,除非重新初始化。
示例代码讲解
初始化无名信号量
sem_init(&unnamed_sema, 0, VALUE);
这里初始化了一个信号量unnamed_sema
,初始值为VALUE
,pshared
值为0,表示此信号量仅在线程之间共享。
线程函数
void* thread_function(void *arg) {
sem_wait(&unnamed_sema); // 等待信号量
printf("Decrease semaphore by 1\n");
counter += 1; // 修改共享变量
printf("Job %d started\n", counter);
sleep(2); // 模拟任务执行
printf("Job %d finished\n", counter);
sem_post(&unnamed_sema); // 增加信号量
return NULL;
}
- sem_wait:等待并减小信号量值。线程进入临界区之前,信号量的值会减1,如果信号量值为0,线程会阻塞,直到其他线程释放信号量。
- 临界区操作:
counter
是共享资源,多个线程要互斥地访问它。这里通过信号量确保只有一个线程可以进入临界区。 - sem_post:离开临界区后,释放信号量,将信号量的值加1,允许其他线程进入临界区。
创建线程并等待完成
pthread_create(&tid[0], NULL, &thread_function, NULL);
pthread_create(&tid[1], NULL, &thread_function, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
创建两个线程并执行thread_function
函数。pthread_join
用于等待线程完成。
销毁信号量
sem_destroy(&unnamed_sema);
总结
- sem_init:创建和初始化无名信号量。
- sem_wait:等待并占用信号量,进入临界区。
- sem_post:释放信号量,离开临界区。
- sem_destroy:销毁信号量,释放资源。
4. 对比
标识方式 | 通过名字(字符串) | 通过指针 |
创建方式 | sem_open() | sem_init() |
共享范围 | 跨进程 | 线程之间或共享内存进程间 |
销毁方式 | sem_close() + sem_unlink() | sem_destroy() |
使用场景 | 进程间同步 | 线程间同步,或同内存进程间同步 |
性能 | 开销稍大,适用于跨进程 | 高效,适用于线程同步 |