C语言多线程编程 semaphore 信号量(信号灯)是什么?如何使用?(本文讲解无名信号量,进程内)

470 篇文章 70 订阅
本文详细介绍了信号量(Semaphore)的概念、用途及分类,包括二值信号量和计数信号量。通过示例程序展示了如何在多线程环境中使用信号量进行任务同步,如限制同时执行的任务数量、实现单生产者单消费者模型。信号量作为一种有效的同步机制,相比锁和条件变量,提供了更为简洁的解决方案。
摘要由CSDN通过智能技术生成

semaphore 信号量、信号灯

定义

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

目的

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

分类

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

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

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

函数使用

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

sem_init 创建信号量

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

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

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

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

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

sem_post 信号量的值加1

int sem_post(sem_t *sem);

第一个参数:信号量对象

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

sem_wait 信号量的值加-1

int sem_wait(sem_t *sem);

第一个参数:信号量对象

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

sem_destroy 用完记得销毁哦~

int sem_destroy(sem_t *sem);

第一个参数:信号量对象

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

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

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

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
//#include <windows.h>

#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;
}

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]  !!!

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

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

(test_semaphore_1_1.c)

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include "unistd.h"
#include <string.h>

#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;
}

编译运行:

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

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

在这里插入图片描述

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

(略,后更。。。)

总结

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

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

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

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dontla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值