网络编程(39)—— 使用信号量semaphore进行多线程同步
2016-12-17 11:26
495 查看
一、Semaphore相关函数
之前介绍过了多线程中利用互斥mutex控制多线程中对临界区的访问方法,本文主要介绍下利用信号量semaphore控制线程对临界区的访问。首先,我们先看一下semaphore相关的函数:
1.1 头文件
#include<semaphore.h>
与之前互斥mutex所引用的头文件pthread.h不同,semaphore的相关函数在semaphore.h头文件中声明(在/usr/include/目录中)。
1.2 初始化函数
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem是我们要创建的信号量的地址。sem_t的本质是一个联合体union,其定义在/usr/bits/semaphore.h文件中,请注意这个不是我们引入的semaphore.h文件。这个头文件只包含了对sem_t的定义,没有其他内容。
typedef union { char __size[__SIZEOF_SEM_T]; long int __align; } sem_t;
pshraed是semaphore需要跨越使用的进程数,设置0时只在一个进程中使用,设置1时可以在两个进程中使用,以此类推。现在明白为什么定义semaphore相关的函数需要在另用一个semaphore.h文件了吧,因为信号量是可以在多进程中使用的,而mutex只能在单进程中的多线程中使用。
value是设置信号量设置的初始值,也是信号量的最大值。如果把临界区的锁定比喻成一把锁,那么信号量就是一把钥匙,而这里的value值相当于钥匙的数量。当一个线程需要访问一个临界区的时候,需要拿一把钥匙,那么信号量的值就会减1。当信号量的值减到0时其他线程便无法访问临界区了,直到已经访问过的线程交出钥匙。当value值设置成1时,显然信号量的作用就类似于mutex了。
1.3 销毁函数
int sem_destroy(sem_t *sem);
销毁函数比较简单,sem就是要销毁的信号量的地址值。
1.4 等待信号量函数
int sem_wait(sem_t *sem);
等待函数就是去打开锁的操作,类似于mutex的pthread_mutex_lock()函数,在进入临界区前调用。如果钥匙数为0(semaphore的值为0),它将会阻塞;如果钥匙数不为0(semaphore的值不为0),调用它后信号量的值将会减1.
1.5 释放信号量函数
int sem_post(sem_t* sem);
释放信号量的函数类似mutex中的pthread_mutex_unlock()函数,在离开临界区后调用。每调用一次信号量的值就会加1.
二、semaphore的使用举例
2.1 利用semaphore控制多线程中临界区的访问
第一个例子我们要把之前利用mutex控制的多线程例子改成用semaphore进行控制:#include<stdio.h> #include<pthread.h> #include<semaphore.h> int num=0; sem_t sem; void* pthread_main1() { int i=0; sem_wait(&sem); for(i=0;i<1000000000;i++) { --num; } sem_post(&sem); } void* pthread_main2() { int i=0; sem_wait(&sem); for(i=0;i<1000000000;i++) { ++num; } sem_post(&sem); } int main() { int i=0; sem_init(&sem,0,1); pthread_t pid[2]; pthread_create((void*)&(pid[0]),NULL,(void*)pthread_main1,NULL); pthread_create((void*)&(pid[1]),NULL,(void*)pthread_main2,NULL); for(i=0;i<2;i++) { pthread_join(pid[i],NULL); } printf("%d\n",num); sem_destroy(&sem); return 0; }
第5行,声明了一个sem_t类型的全局变量sem。
第30行,对sem进行初始化,因为程序在单进程中运行,所以sem_init的第二个参数为0;第三个参数设置成1,这样就类似于mutex的效果,通过调用sem_wait和sem_post,信号量的值始终在1和0之间进行变动。
第9行和第19行利用sem_wait()等待信号量的值大于0,由于信号量的值在0和1之间变动,这两行始终只有一行代码会被执行,另一行代码进行阻塞。
第14行和第24行利用sem_post()函数释放信号量。
第39行,利用sem_wait()函数销毁信号量。
上述示例是演示利用semaphore替代mutex进行临界区访问控制的方法,最终也会达到了mutex一样的控制效果,结果如下:
[Hyman@Hyman-PC semphare]$ gcc sem.c -lpthread [Hyman@Hyman-PC semphare]$ ./a.out 0
2.2 利用semaphore控制socket服务端的最大客户端连接数
接下来,我们编写一个使用信号量控制客户端连接数的多线程服务端,与之前的服务端不同,我们使用定义一个值为5的信号量。sem_t sem; semaphore_init(&sem,0,5);
因为无需要多进程,所以初始化函数中第二个参数设置成0,最后一个参数设置成5,用来限制连接的客户端数。先把源代码放上来,后面再进行分析:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #include<semaphore.h> #include<sys/socket.h> #include<arpa/inet.h> #define BUF_SIZE 1024 sem_t sem; void* pthread_main(void* param) { int str_len; char buf[BUF_SIZE]; int clnt_sock=*((int*)param); while(1) { str_len=read(clnt_sock,buf,BUF_SIZE); if(str_len<=0) break; write(clnt_sock,buf,str_len); } sem_post(&sem); close(clnt_sock); } int main(int argc,char* argv[]) { int serv_sock,clnt_sock; struct sockaddr_in serv_addr,clnt_addr; int clnt_addr_sz; pthread_t thread_id; if(argc!=2) { printf("Usage %s <port>\n",argv[0]); exit(1); } serv_sock=socket(AF_INET,SOCK_STREAM,0); memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); serv_addr.sin_port=htons(atoi(argv[1])); bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); listen(serv_sock,10); sem_init(&sem,0,5); while(1) { sem_wait(&sem); clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_sz); pthread_create(&thread_id,NULL,(void*)pthread_main,&clnt_sock); } //pthread_join(thread_id,NULL); sem_destroy(&sem); return 0; }
第10行,创建了一个信号量sem,因为在每个线程中都有使用,所以我们把它声明成了全局变量
第50行,调用sem_init()对sem进行初始化,由于未涉及到多进程,所以第二个参数是0;由于想限制最多接收5个客户端的连接,所以第三个参数设置为5.
第53行,调用sem_wait()等待信号量,如果信号量树大于0,则执行下面的代码;否则发生阻塞;
第23行,调用sem_post()释放信号量,只有在一个线程中客户端结束连接后才会释放一个信号量,这样才能有新的客户端连接。
运行时,当运行到第5个客户端时,一直输入正常,如下所示:
[Hyman@Hyman-PC semphare]$ ./clnt 127.0.0.1 9991 5 the message from server:5当运行第6个客户端时,服务端已经开始阻塞,客户端发给服务端的数据没得到回应:
[Hyman@Hyman-PC semphare]$ ./clnt 127.0.0.1 9991 6
以上是对单进程中semaphore的使用一个简单的介绍,下一文将会介绍semaphore在多进程中的使用方法。
Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
Git clone
git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL39
相关文章推荐
- Java并发编程中级篇(二):使用Semaphore信号量进行多个资源并发控制
- Android使用信号量Semaphore进行多线程任务调度
- Java并发编程中级篇(一):使用Semaphore信号量进行并发控制
- Android使用信号量Semaphore进行多线程任务调度
- 网络编程(40)—— 使用信号量semaphore进行多进程间的同步
- 互联网进行限流策略的Semaphore信号量使用
- 使用信号量 进行windows线程同步 (Semaphore)
- Linux下通过共享内存进行进程间通信,进程间同步使用信号量来实现
- Java中使用信号量——Semaphore
- NET多线程同步方法详解(五):信号量(Semaphore)
- 使用sem_post信号量进行线程同步
- Android网络编程(使用socket进行通信)
- 使用信号量进行线程间同步
- 使用Semaphore进行进程同步
- Linux 设备驱动--- 并发 之- 信号量 --- semaphore --- down_interruptible --- 按键信号量使用
- Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
- 信号量 Semaphore的使用介绍
- 信号量(Semaphore),实现方法的并发限量使用
- C#使用Monitor类、Lock和Mutex类进行多线程同步
- C#使用Monitor类、Lock和Mutex类进行多线程同步