linux20 线程同步--信号量
为什么需要线程同步,我们给出以下代码:
我们创建五个线程分别对val进行++打印1000次:
#include<stdio.h>#include<pthread.h>#include<unistd.h>#include<stdlib.h>#include<semaphore.h>int val=0;void* fun(void*arg){for(int i=0;i<10000;i++){val++;printf("val=%d\n",val);}}int main(){pthread_t id[5];int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);}运行结果:

我们可以通过观察发现次代码并没有符合我们的预期打印50000,这是因为多个线程同时对val++造成的,为了解决这种现象我们利用信号量来来进行控制。
信号量是一种同步原语,本质上是一个计数器,用于控制多个线程对共享资源的访问
- 计数器机制:信号量值表示可用资源的数量
- 原子操作:P(等待)和V(发送)操作是原子的
- 阻塞机制:当资源不足时,线程会被挂起等待
- 唤醒机制:资源释放时会自动唤醒等待的线程
#include <semaphore.h>// 初始化未命名信号量(线程间)int sem_init(sem_t *sem, int pshared, unsigned int value);// 打开/创建命名信号量(进程间)sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);// 关闭命名信号量int sem_close(sem_t *sem);//销毁未命名信号量int sem_destroy(sem_t*sem);// 删除命名信号量int sem_unlink(const char *name);//sem_wait P操作int sem_wait(sem_t *sem);//sem_post V操作int sem_post(sem_t *sem);13.2.1线程-信号量的初始化
// 初始化未命名信号量(线程间)int sem_init(sem_t *sem, int pshared, unsigned int value);参数说明:
- pshared :0表示线程间共享,非0表示进程间共享
- value :信号量的初始值
13.2.2线程信号量操作函数
// P操作 - 等待信号量int sem_wait(sem_t *sem); // 阻塞版本int sem_trywait(sem_t *sem); // 非阻塞版本int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); // 超时版本// V操作 - 释放信号量int sem_post(sem_t *sem);// 获取信号量当前值int sem_getvalue(sem_t *sem, int *sval);13.2.3 销毁线程信号量
// 销毁信号量int sem_destroy(sem_t *sem);13.2.4总结
对于上面的代码我们进行修改:
#include<stdio.h>#include<pthread.h>#include<unistd.h>#include<stdlib.h>#include<semaphore.h>int val=0;sem_t sem;void* fun(void*arg){for(int i=0;i<10000;i++){sem_wait(&sem);val++;sem_post(&sem);printf("val=%d\n",val);}}int main(){pthread_t id[5];int i=0;sem_init(&sem,0,1);for(;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}sem_destroy(&sem);exit(0);}运行代码结果:

可以观察到使用信号量可以达到预期值50000
