应用select 函数控制多线程中子线程结束方法

年前做一个化肥行业条形码扫描的工控机产品,用到很多串口通信和多线程,程序中需要经常开辟新线程完成串口数据采集工作,按照以前习惯用read方法读取发现线程始终阻塞在read函数处,而linux的线程机制又无法从主线程控制子线程的结束,因此必须用一种方法,使得阻塞线程能够在程序的控制下安全退出。经过研究,决定用select函数实现这个功能。

 

select 函数在linux的通信编程中经常使用,这个函数提供了一种机制,可以监测文件IO的数据变化,并在监测期间按照设定的时间阻塞线程,一旦文件IO有数据变化或者设定时间结束,则返回一个结果。select函数的原型是:

 

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

1、参数分析

1.1int maxfdp: select监视的文件句柄数,设为要监视各文件中的最大文件描述法号加一。

1.2readfds:select监视的可读文件句柄集合

1.3writefds:select监视的可写文件句柄集合

1.4errorfds:select监视的异常处理文件句柄集合

 

这三个参数的数据类型都是fd_set,这实际是一个long型的数组,数组的每个元素与文件句柄建立联系,一旦其中的某个文件IO有数据变换,内核就会修改fd_set,来通知进程哪个文件起了变化。

1.5timeout:这是select机制设定阻塞即时的参数。timeval结构体有两个成员,timeout.tv_sec和timeout.tv_usec,分别设置阻塞时间的秒和微秒,这个是很精确的。

timeout有三种形式,当为NULL时,将函数至于阻塞状态,直到等待某个文件可读写。若将时间设置为0秒0微妙,则函数运行到此时不阻塞;当timeout设置有一个正时间时,函数阻塞这个时间长度返回。

 

2、select函数的返回值

-1 select函数错误

正值 文件可读写

0 等待时间超时,没有可读写的文件

 

3、与该函数有关的结构体
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。 
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。 
FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。
FD_ZERO(fdset *fdset):将fdset的结构体清空

 

4、控制线程退出实例

 

  

 

[cpp]  view plain copy
  1. pthread_mutex_t g_mutex;  
  2. int g_flag_quit;  
  3. static inline void gSetQuit(void)  
  4. {  
  5.     pthread_mutex_lock(&g_mutex);  
  6.     g_flag_quit = 1;  
  7.     pthread_mutex_unlock(&g_mutex);  
  8. }  
  9.   
  10. static inline int gIsQuit(void)  
  11. {  
  12.     int quit;  
  13.   
  14.     pthread_mutex_lock(&g_mutex);  
  15.     quit = g_flag_quit;  
  16.     pthread_mutex_unlock(&g_mutex);  
  17.     return quit;  
  18. }  
  19. void ThrFxn(void)  
  20. {  
  21.     int serial_fd = open("/dev/ttyS0",O_WDRD);  
  22.   
  23.     while(!gIsQuit())  
  24.     {  
  25.         timeval timeout;  
  26.         fd_set rfdset;  
  27.         FD_ZERO(&rfdset);  
  28.         FD_SET(serial_fd, &rfdset);  
  29.         timeout.tv_sec = 0;  
  30.         timeout.tv_usec = 500000;  
  31.   
  32.         rtn = select(1+fd_com1, &rfdset, NULL, NULL, &timeout);  
  33.   
  34.     if(rtn == -1)  
  35.     {  
  36.       perror("select:error/n");  
  37.           return;  
  38.     }  
  39.     else if(rtn == 0)  
  40.     {   
  41.           printf("thread time out!/n");  
  42.           continue;  
  43.         }  
  44.         else  
  45.     {  
  46.            if (FD_ISSET(serial_fd, &rfdset))  
  47.        {  
  48.         rtn = read(serial_fd, mbuffer, READ_SIZE);  
  49.                 //读取数据后处理  
  50.            }  
  51.         }  
  52. }  
  53.   
  54. int main()  
  55. {  
  56.   pthread_t pthr;  
  57.   int i=5;  
  58.   
  59.   pthread_create(&pthr, NULL, ThrFxn, NULL);  
  60.   
  61.   while(!IsQuit())  
  62.   {  
  63.      if(i==0)  
  64.      { gSetQuit();  
  65.      }  
  66.      sleep(1);  
  67.      printf("control thread quit %s sencons/n", i--);  
  68.   
  69.   }  
  70. }  

 

这个例子中共有四个函数,主函数创建子线程,设定倒计时,控制子线程结束。子线程函数完成子线程的功能。

gSetQuit()和gIsQuit()两个函数是设定全局退出信号变量的函数和检测退出信号的函数。SUN公司推荐,在有循环的地方,每次循环都用这种方式检测全局变量,在需要结束线程的地方设置全局变量,可以实现方便的多线程控制和同步问题。在对全局退出信号变量操作时,必须用互斥锁保护临界资源。

值得一提的是,在使用select函数时,每次循环前都要清空文件描述符集rfdset,才能保证每次循环开始时select函数正常检测文件IO 的变化。

 

我在程序中调试的经验是,select函数的阻塞时间可根据系统的实时响应需求来设定,比如一般的按键可接受在数百毫秒内完成响应,那么阻塞时间可以设置为100ms甚至更长。这个例子中,每次阻塞500ms后,若串口没有收到数据,则select返回0,并结束本次循环,判断gIsQuit()。

 

本文记述的线程退出方式安全稳定、程序逻辑设计简单,在许多场合,当需要程序员经常主动结束线程,并手动释放线程资源时,可以采用这种方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目简介: 采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make DB:Sqlite 3 Time:2015-12-09 ~ 2012-12-21 项目功能架构: 1. 采用client/server结构; 2. 给出客户操作主界面(注册、登录、帮助和退出)、登录后主界面(查看在线列表、私聊、群聊、查看聊天记录、退出); 3. 多客户可同时连接服务器进行自己操作; ##服务器端## 1. server.c:服务器端主程序代码文件; 2. config.h:服务器端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:服务器端公共函数的实现文件; 4. list.c:链表实现文件,用于维护在线用户链表的添加、更新、删除操作; 5. register.c:服务器端实现用户注册; 6. login.c:服务器端实现用户登录; 7. chat.c:服务器端实现用户的聊天互动操作; 8. Makefile:服务器端make文件控制台执行make命令可直接生成可执行文件server ##客户端## 1. client.c:客户端主程序代码文件; 2. config.h:客户端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:客户端公共函数的实现文件; 4. register.c:客户端实现用户注册; 5. login.c:客户端实现用户登录; 6. chat.c:客户端实现用户的聊天互动操作; 7. Makefile:客户端make文件控制台执行make命令可直接生成可执行文件client;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值