• C语言多线程编程 semaphore 信号量(信号灯)是什么?如何使用?


    semaphore 信号量、信号灯

    定义

    有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。

    目的

    类似计数器,常用在多线程同步任务上,信号量可以在当前线程某个任务完成后,通知别的线程,再进行别的任务。

    分类

    二值信号量:信号量的值只有0和1,这和互斥量很类似,若资源被锁住,信号量的值为0,若资源可用,则信号量的值为1;

    计数信号量:信号量的值在0到一个大于1的限制值之间,该计数表示可用的资源的个数。

    信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为1就变成互斥锁Mutex,即同时只能有一个任务可以访问信号量保护的共享资源

    函数使用

    首先需要include 这个库,没啥好说的,除非你自己实现内部函数。和互斥锁一样,也是四大金刚。(编译的时候需要加上-lpthread

    sem_init 创建信号量

    int sem_init(sem_t *sem, int pshared, unsigned int value);
    sem_post
    
    • 1
    • 2

    第一个参数:指向的信号对象

    第二个参数:控制信号量的类型,如果其值为0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享

    第三个参数:信号量sem的初始值

    返回值:success为0,failure为-1

    sem_post 信号量的值加1

    int sem_post(sem_t *sem);
    
    • 1

    第一个参数:信号量对象

    返回值:success为0,failure为-1

    sem_wait 信号量的值加-1

    int sem_wait(sem_t *sem);
    
    • 1

    第一个参数:信号量对象

    返回值:success为0,failure为-1

    sem_destroy 用完记得销毁哦~

    int sem_destroy(sem_t *sem);
    
    • 1

    第一个参数:信号量对象

    返回值:success为0,failure为-1

    示例程序1:进行三个下载任务,但是最多选择同时执行二个(创建两个线程)

    (我对原作者的代码进行了一些改造)

    #include 
    #include 
    #include 
    //#include 
    
    #define MAXNUM 2
    sem_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)
    {
    	int ret;
    	//等待信号量的值>0
    	ret = sem_wait(&semDownload);
    	if(ret != 0)
    	{
    		printf("error, sem_wait failed\n");
    	}
    	printf("==============  Downloading Task 1  ============== \n");
    	sleep(5);
    	printf("==============    Finished Task 1   ============== \n");
    	g_phreadNum--;
    	//等待线程结束 
    	//pthread_join(a_thread, NULL);
    }
    
    void *func2(void *arg)
    {
    	int ret;
    	ret = sem_wait(&semDownload);
    	if(ret != 0)
    	{
    		printf("error, sem_wait failed\n");
    	}
    	printf("==============  Downloading Task 2  ============== \n");
    	sleep(3);
    	printf("==============    Finished Task 2   ============== \n");
    	g_phreadNum--;
    	//pthread_join(b_thread, NULL);
    }
    
    void *func3(void *arg)
    {
    	int ret;
    	ret = sem_wait(&semDownload);
    	if(ret != 0)
    	{
    		printf("error, sem_wait failed\n");
    	}
    	printf("==============  Downloading Task 3  ============== \n");
    	sleep(1);
    	printf("==============    Finished Task 3   ============== \n");
    	g_phreadNum--;
    	//pthread_join(c_thread, NULL);
    }
    
    int main()
    {
    	int ret;
    	int taskNum;
    	InputInfo();
    
            //初始化信号量
    	ret = sem_init(&semDownload, 0, 0);
    	if(ret != 0)
    	{
    		printf("error, sem_init failed\n");
    	}
    	while (scanf("%d", &taskNum) != EOF) {
    		//输入0,判断是否正常退出
    		if (taskNum == 0)
    		{
    			if (g_phreadNum <= 1) 
    			{
    				break;
    			}
    			else
    			{
    				printf("Can not quit, casue count of threads is [%d]\n", g_phreadNum - 1);
    			}	
    		}
    
    		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;
    		}
    
    		//用户选择下载Task
    		switch (taskNum)
    		{
    		case 1:
    			//创建线程1
    			pthread_create(&a_thread, NULL, func1, NULL);
    			//信号量+1,进而触发fun1的任务
    			ret = sem_post(&semDownload);
    			if(ret != 0)
    			{
    				printf("error, sem_post failed\n");
    			}
    			//总线程数+1
    			g_phreadNum++;
    			break;
    		case 2:
    			pthread_create(&b_thread, NULL, func2, NULL);
    			ret = sem_post(&semDownload);
    			if(ret != 0)
    			{
    				printf("error, sem_post failed\n");
    			}
    			g_phreadNum++;
    			break;
    		case 3:
    			pthread_create(&c_thread, NULL, func3, NULL);
    			ret = sem_post(&semDownload);
    			if(ret != 0)
    			{
    				printf("error, sem_post failed\n");
    			}
    			g_phreadNum++;
    			break;
    		default:
    			printf("!!! eroor task [%d]  !!!\n", taskNum);
    			break;
    		}
    
    	}
    
    	//销毁信号量
    	sem_destroy(&semDownload);
    	if(ret != 0)
    	{
    		printf("error, sem_destroy failed\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    CentOS编译运行结果:

    [root@localhost 20220816]# ./a.out 
    ****************************************
    *** which task you want to download? ***
    *** you can enter [1-3],[0] is done  ***
    ****************************************
    1
    your choose Downloading Task [1]
    ==============  Downloading Task 1  ============== 
    ==============    Finished Task 1   ============== 
    2
    your choose Downloading Task [2]
    ==============  Downloading Task 2  ============== 
    3
    your choose Downloading Task [3]
    ==============  Downloading Task 3  ============== 
    ==============    Finished Task 2   ============== 
    4==============    Finished Task 3   ============== 
    
    your choose Downloading Task [4]
    !!! eroor task [4]  !!!
    5
    your choose Downloading Task [5]
    !!! eroor task [5]  !!!
    1
    your choose Downloading Task [1]
    ==============  Downloading Task 1  ============== 
    2
    your choose Downloading Task [2]
    ==============  Downloading Task 2  ============== 
    3
    your choose Downloading Task [3]
    !!! You've reached a limit on the number of threads !!!
    ==============    Finished Task 1   ============== 
    ==============    Finished Task 2   ============== 
    4
    your choose Downloading Task [4]
    !!! eroor task [4]  !!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    示例程序:单生产者单消费者模型

    简介:一个线程生产,另一个线程消费,线程A生产完后立马给线程B发信号,线程B收到后立即回应线程A,告诉它我收到了,发送100000次

    (test_semaphore_1_1.c)

    #include 
    #include 
    #include 
    #include "unistd.h"
    #include 
    
    #define printf(format, ...)                                                          	\
        do {                                                                                \
                char str[1024] = {0};                                                       \
                struct timeval tv;                                                          \
                struct tm* t;                                                               \
                gettimeofday(&tv, NULL);                                                    \
                t = localtime(&tv.tv_sec);                                                  \
                sprintf(str,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ",                       \
                                1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday,               \
                                t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec / 1000);       \
                printf("%s", str);                                                          \
                printf("#%d "format, __LINE__, ##__VA_ARGS__);                              \
           } while (0)
    
    #define LIM (100000)
    
    sem_t sem, sem_R;
    int count = 0;
    int count_R = 0;
    
    void* f_producer(void *arg)
    {
    	sleep(1);
    
    	int ret;
    	while(1)
    	{	
    		printf("[%s] produce start\n", __FUNCTION__);
    		//信号量+1,进而触发fun1的任务
    		printf("[%s] produce finish, sem_post\n", __FUNCTION__);
    		
    		ret = sem_post(&sem);
    		if(ret != 0)
    		{
    			printf("[%s] error, sem_post failed\n", __FUNCTION__);
    			break;
    		}
    		
    		count++;
    		printf("\n[%s] count = %d\n", __FUNCTION__, count);
    		if(count == LIM)
    		{
    			printf("[%s] break\n", __FUNCTION__);
    			break;
    		}
    
    		printf("[%s] waiting for sem_R\n", __FUNCTION__);
    		ret = sem_wait(&sem_R);
    		if(ret != 0)
    		{
    			printf("[%s] error, sem_wait failed\n", __FUNCTION__);
    			break;
    		}
    		printf("[%s] received sem_R\n", __FUNCTION__);
    	}
    }
    
    void* f_consumer(void *arg)
    {
    	sleep(1);
    
    	int ret;
    	while(1)
    	{
    		printf("[%s] waiting for sem\n", __FUNCTION__);
    		ret = sem_wait(&sem);
    		if(ret != 0)
    		{
    			printf("error, sem_wait failed\n");
    			break;
    		}
    		printf("[%s] received sem\n", __FUNCTION__);
    
    		count_R++;
    		printf("\n[%s] count_R = %d\n", __FUNCTION__, count_R);
    		if(count_R == LIM)
    		{
    			printf("[%s] break\n", __FUNCTION__);
    			break;
    		}
    
    		printf("[%s] return start\n", __FUNCTION__);
    		ret = sem_post(&sem_R);
    		if(ret != 0)
    		{
    			printf("[%s] error, sem_post failed\n", __FUNCTION__);
    			break;
    		}
    	}
    }
    
    
    int main()
    {
    	int ret;
    	pthread_t consumer, producer;
            //初始化信号量
    	ret = sem_init(&sem, 0, 0);
    	if(ret != 0)
    	{
    		printf("error, sem_init failed\n");
    	}
    
    	//创建线程
    	pthread_create(&producer, NULL, f_producer, NULL);
    	pthread_create(&consumer, NULL, f_consumer, NULL);
    
    	ret = pthread_join(producer, NULL);
    	if(ret != 0)
    	{
    		printf("error, join failed, ret = [%d]\n", ret);
    	}
    
    	ret = pthread_join(consumer, NULL);
    	if(ret != 0)
    	{
    		printf("error, join failed, ret = [%d]\n", ret);
    	}
    
    	//销毁信号量
    	sem_destroy(&sem);
    	if(ret != 0)
    	{
    		printf("error, sem_destroy failed\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133

    编译运行:

    [root@localhost 20220817]# gcc test_semaphore_1_1.c -lpthread
    [root@localhost 20220817]# ./a.out 
    
    • 1
    • 2

    结果:
    总共只花费了10秒钟就跑完了,且无信号丢失,还是很稳定的(比那个啥互斥锁+条件变量好用多了lll…)

    在这里插入图片描述

    示例程序:多生生产者多消费者模型

    (略,后更。。。)

    总结

    这用来做线程间通知,不比锁加条件变量好用多了啊!!!!那个一大堆,还挺麻烦的,,,,,,

    参考文章1:C语言多线程编程(三)——信号量

    参考文章2:Linux 系统中的信号量机制

  • 相关阅读:
    Solidity拓展:数学运算过程中数据长度溢出的问题
    基于51单片机步进电机加减速正反转数码管显示( proteus仿真+程序+原理图+设计报告+讲解视频)
    uni-app 之 安装uView,安装scss/sass编译
    quarkus实战之五:细说maven插件
    Redis分布式锁
    【c#】2022创建WEB API接口教程demo
    你需要知道的四个奇怪的育儿冷知识
    笔试算法(一)
    因高额网络费用,Arbitrum 奥德赛活动暂停,Nitro 发行迫在眉睫
    springcloud+nacos+gateway约会
  • 原文地址:https://blog.csdn.net/Dontla/article/details/123066055