首先,祝大家冬至快乐!今天,介绍下信号量,也属于线程同步的一种方式。文字比较多,不好理解,但是一看代码你就懂了。
看懂这次的代码还需要之前的知识~ 补课链接:
信号量 Semaphore
定义:
有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。
目的:
类似计数器,常用在多线程同步任务上,信号量可以在当前线程某个任务完成后,通知别的线程,再进行别的任务。
分类:
- 二值信号量: 信号量的值只有 0 和 1,这和互斥量很类似,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1;
- 计数信号量: 信号量的值在 0 到一个大于 1 的限制值之间,该计数表示可用的资源的个数。
信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁 Mutex,即同时只能有一个任务可以访问信号量保护的共享资源
函数使用:
首先需要 include
sem_init
简述:创建信号量
第一个参数:指向的信号对象
第二个参数:控制信号量的类型,如果其值为 0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享
第三个参数:信号量 sem 的初始值
返回值:success 为 0,failure 为 - 1
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem_post
简述:信号量的值加 1
第一个参数:信号量对象
返回值:success 为 0,failure 为 - 1
int sem_post(sem_t *sem);
sem_wait
简述:信号量的值加 - 1
第一个参数:信号量对象
返回值:success 为 0,failure 为 - 1
int sem_wait(sem_t *sem);
sem_destroy
简述:用完记得销毁哦~
第一个参数:信号量对象
返回值:success 为 0,failure 为 - 1
int sem_destroy(sem_t *sem);
举例
说明:你可以进行三个下载任务,但是最多选择同时执行二个(创建两个线程)。直接看 main 函数即可,信号量的逻辑都在里面,在实际代码中最好,所有的线程和信号量的创建、释放都要进行校验,这里为了方便阅读,减少代码行数,就不进行校验了。
#include <stdio.h>#include <pthread.h>#include <semaphore.h>#include <windows.h>#define MAXNUM 2sem_t semDownload;pthread_t a_thread, b_thread, c_thread;int g_phreadNum = 1;void InputInfo(void){printf("****************************************\n");printf("*** which task you want to download? ***\n");printf("*** you can enter [1-3],[0] is done ***\n");printf("****************************************\n");}void *func1(void *arg){//等待信号量的值>0sem_wait(&semDownload);printf("============== Downloading Task 1 ============== \n");Sleep(5000);printf("============== Finished Task 1 ============== \n");g_phreadNum--;//等待线程结束pthread_join(a_thread, NULL);}void *func2(void *arg){sem_wait(&semDownload);printf("============== Downloading Task 2 ============== \n");Sleep(3000);printf("============== Finished Task 2 ============== \n");g_phreadNum--;pthread_join(b_thread, NULL);}void *func3(void *arg){sem_wait(&semDownload);printf("============== Downloading Task 3 ============== \n");Sleep(1000);printf("============== Finished Task 3 ============== \n");g_phreadNum--;pthread_join(c_thread, NULL);}int main(){int taskNum;InputInfo();while (scanf("%d", &taskNum) != EOF) {//输入0,判断是否正常退出if (taskNum == 0 && g_phreadNum <= 1) {break;}if (taskNum == 0){printf("Can not quit, casue count of threads is [%d]\n", g_phreadNum - 1);}//初始化信号量sem_init(&semDownload, 0, 0);printf("your choose Downloading Task [%d]\n", taskNum);//线程数超过2个则不下载if (g_phreadNum > MAXNUM) {printf("!!! You've reached a limit on the number of threads !!!\n");continue;}//用户选择下载Taskswitch (taskNum){case 1://创建线程1pthread_create(&a_thread, NULL, func1, NULL);//信号量+1,进而触发fun1的任务sem_post(&semDownload);//总线程数+1g_phreadNum++;break;case 2:pthread_create(&b_thread, NULL, func2, NULL);sem_post(&semDownload);g_phreadNum++;break;case 3:pthread_create(&c_thread, NULL, func3, NULL);sem_post(&semDownload);g_phreadNum++;break;default:printf("!!! eroor task [%d] !!!\n", taskNum);break;}}//销毁信号量sem_destroy(&semDownload);return 0;}
输出:
红色箭头指的是你的输入

如果你是初学者,你可能会有这样的
疑问: 为啥不直接把 “下载任务” 放在 switch-case 内,而要搞个信号量这么麻烦呢?
我给你的答案是: 你自己实现一次,就把函数里的内容剪切到 case 里,然后,运行下代码就知道为啥了。
https://zhuanlan.zhihu.com/p/98717838
