PV 原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量 sem,当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,
信号量互斥操作 信号量同步操作
函数说明。
Linux 实现了 POSIX 的无名信号量,主要用于线程间的互斥与同步。这里主要介绍几个常见函数。
sem_init()用于创建一个信号量,并初始化它的值。
sem_wait()和 sem_trywait()都相当于 P 操作,在信号量大于零时它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait()将会阻塞进程,而 sem_trywait()则会立即返回。
sem_post()相当于 V 操作,它将信号量的值加一同时发出信号来唤醒等待的进程。
sem_getvalue()用于得到信号量的值。
sem_destroy()用于删除信号量。
demo1: 用信号量同步机制实现3个线程之间的有序执行,只是执行顺序是跟创建线程的顺序相反,具体代码思路是让3个线程重用同一个执行函数。每个线程都有三次循环(可以看成3个小任务),每次循环之间等待1~10s的时间,意义在于模拟每个任务的到达时间是随机的,没有任何特定的规律。
#include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<semaphore.h> #define THREAD_NUMBER 3 #define REPEAT_NUMBER 3 #define DELAY_TIME_LEVELS 10.0 //定义一个信号量数组 设置多个信号量用于线程的同步操作 sem_t sem[THREAD_NUMBER]; void *thrd_func(void *arg) { int thrd_num = (int)arg; int delay_time = 0; int count = 0; sem_wait(&sem[thrd_num]);//相当信号量大于0时减一,信号量小于0时阻塞线程 printf("Thread %d is starting\n", thrd_num); for(count = 0; count < REPEAT_NUMBER; count++) { //模拟循环等待时间 delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1; sleep(delay_time); printf("Thread %d: job %d delay = %d\n", thrd_num, count, delay_time); } printf("Thread %d finished\n", thrd_num); pthread_exit(NULL); } int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void* thrd_ret; srand(time(NULL)); for(no = 0; no < THREAD_NUMBER; no++) { sem_init(&sem[no], 0, 0);//用于创建一个信号量,并初始化它的值 res = pthread_create(&thread[no], NULL, thrd_func, (void*)no); if(res != 0) { printf("Create thread %d failed\n", no); exit(res); } } printf("Create treads success\n Wating for threads to finish...\n"); //将信号量的值加一同时发出信号来唤醒等待的进程 sem_post(&sem[THREAD_NUMBER - 1]); for(no = THREAD_NUMBER - 1; no >= 0; no--) { res = pthread_join(thread[no], &thrd_ret);//阻塞等待线程结束 if(!res) { printf("Thread %d joined\n", no); } else { printf("Thread %d join fail\n", no); } //相当于V操作,它将信号量的值加1同时发出信号唤醒等待的线程 sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]); } for(no = 0; no < THREAD_NUMBER; no++) { sem_destroy(&sem[no]);//删除信号量 } return 0; }