信号量广泛用于进程或线程建的同步和互斥,信号量本质上是一个非负整数计数器,它被用来控制对公共资源的访问。编程时,可根据操作信号量值的结果判断是否对公共资源具有访问权限,当信号量值大于0
时,则可以访问,否则将阻塞。PV原语
是对信号量的操作,一次P
操作是信号量减1
,一次V
操作使信号量加1
。
#if __WORDSIZE == 64
# define __SIZEOF_SEM_T 32
#else
# define __SIZEOF_SEM_T 16
#endif
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
} sem_t;
int sem_init(sem_t *sem, int pshared, unsigned int value);
无名信号量
pshared
指示该信号量是在一个进程内的所有线程
共享还是在进程间
共享
0
,信号量被一个进程内的所有线程共享非0
,信号量在进程间共享value
指示信号量额初始值int sem_destroy(sem_t *sem);
无名信号量
sem_init
初始化的信号量,使用该函数进行销毁int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
sem_wait
使信号量执行一次P操作
。
减一
后,立即返回P操作
或者一个信号处理中断被调用sem_trywait
除了当前信号量不可执行P操作
时,该函数直接返回,且将errno
设置为EAGAIN
外,与sem_wait
相同
sem_timedwait
除了当前信号量不可执行P操作
时,该函数将等待参数abs_timeout
指定的时间外,与sem_wait
相同。
P操作
,则正常返回P操作
,则调用返回,且将errno
设置为ETIMEDOUT
int sem_post(sem_t *sem);
加1
注意
- 信号量用于互斥:不管多少个任务互斥,只需要一个信号量。
- 对于每一个线程中的任务,都是先执行
P操作(sem_*wait)
,后执行V操作(sem_post)
- 任务(线程)执行的先后顺序是不确定的
要求
- 任务(线程)一连续输出
0-4
- 任务(线程)二连续输出
10-14
- 任务(线程)三连续输出
20-24
- 任务(线程)的执行顺序无要求
源码
#include
#include
#include
#include
#include
sem_t sem;
void *start_routine_01(void *ptr)
{
sem_wait(&sem);
for (size_t i = 0; i < 5; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem);
return (void *)NULL;
}
void *start_routine_02(void *ptr)
{
sem_wait(&sem);
for (size_t i = 10; i < 15; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem);
return (void *)NULL;
}
void *start_routine_03(void *ptr)
{
sem_wait(&sem);
for (size_t i = 20; i < 25; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem);
return (void *)NULL;
}
int main(int argc, char const *argv[])
{
sem_init(&sem, 0, 1);
pthread_t thread_id_01, thread_id_02, thread_id_03;
pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
pthread_create(&thread_id_02, NULL, start_routine_02, NULL);
pthread_create(&thread_id_03, NULL, start_routine_03, NULL);
pthread_join(thread_id_01, NULL);
pthread_join(thread_id_02, NULL);
pthread_join(thread_id_03, NULL);
sem_destroy(&sem);
exit(EXIT_SUCCESS);
}
输出
读线程(139881171838720)获取全局变量当前值(0)
读线程(139881171838720)获取全局变量当前值(1)
读线程(139881171838720)获取全局变量当前值(2)
读线程(139881171838720)获取全局变量当前值(3)
读线程(139881171838720)获取全局变量当前值(4)
读线程(139881163446016)获取全局变量当前值(10)
读线程(139881163446016)获取全局变量当前值(11)
读线程(139881163446016)获取全局变量当前值(12)
读线程(139881163446016)获取全局变量当前值(13)
读线程(139881163446016)获取全局变量当前值(14)
读线程(139881155053312)获取全局变量当前值(20)
读线程(139881155053312)获取全局变量当前值(21)
读线程(139881155053312)获取全局变量当前值(22)
读线程(139881155053312)获取全局变量当前值(23)
读线程(139881155053312)获取全局变量当前值(24)
注意
- 有多少个任务,就需要多少个信号量
- 初始化时,最先执行的任务对应的信号量设置为
1
,其余信号量设置为0
- 在每个任务(线程)中,先
P操作(sem_*wait)
自己,后V操作(sem_post)
下一个任务
要求
- 任务(线程)一连续输出
0-4
- 任务(线程)二连续输出
10-14
- 任务(线程)三连续输出
20-24
- 任务(线程)的执行顺序必须是:
- 任务(线程)一
- 任务(线程)二
- 任务(线程)三
源码
#include
#include
#include
#include
#include
sem_t sem_01, sem_02, sem_03;
void *start_routine_01(void *ptr)
{
sem_wait(&sem_01);
for (size_t i = 0; i < 5; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem_02);
return (void *)NULL;
}
void *start_routine_02(void *ptr)
{
sem_wait(&sem_02);
for (size_t i = 10; i < 15; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem_03);
return (void *)NULL;
}
void *start_routine_03(void *ptr)
{
sem_wait(&sem_03);
for (size_t i = 20; i < 25; i++)
{
printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
sleep(1);
}
sem_post(&sem_01);
return (void *)NULL;
}
int main(int argc, char const *argv[])
{
sem_init(&sem_01, 0, 1);
sem_init(&sem_02, 0, 0);
sem_init(&sem_03, 0, 0);
pthread_t thread_id_01, thread_id_02, thread_id_03;
pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
pthread_create(&thread_id_02, NULL, start_routine_02, NULL);
pthread_create(&thread_id_03, NULL, start_routine_03, NULL);
pthread_join(thread_id_01, NULL);
pthread_join(thread_id_02, NULL);
pthread_join(thread_id_03, NULL);
sem_destroy(&sem_01);
sem_destroy(&sem_02);
sem_destroy(&sem_03);
exit(EXIT_SUCCESS);
}
输出
读线程(140602965497600)获取全局变量当前值(0)
读线程(140602965497600)获取全局变量当前值(1)
读线程(140602965497600)获取全局变量当前值(2)
读线程(140602965497600)获取全局变量当前值(3)
读线程(140602965497600)获取全局变量当前值(4)
读线程(140602957104896)获取全局变量当前值(10)
读线程(140602957104896)获取全局变量当前值(11)
读线程(140602957104896)获取全局变量当前值(12)
读线程(140602957104896)获取全局变量当前值(13)
读线程(140602957104896)获取全局变量当前值(14)
读线程(140602948712192)获取全局变量当前值(20)
读线程(140602948712192)获取全局变量当前值(21)
读线程(140602948712192)获取全局变量当前值(22)
读线程(140602948712192)获取全局变量当前值(23)
读线程(140602948712192)获取全局变量当前值(24)