应用select 函数控制多线程中子线程结束方法
2010-02-14 18:27
363 查看
年前做一个化肥行业条形码扫描的工控机产品,用到很多串口通信和多线程,程序中需要经常开辟新线程完成串口数据采集工作,按照以前习惯用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、控制线程退出实例
这个例子中共有四个函数,主函数创建子线程,设定倒计时,控制子线程结束。子线程函数完成子线程的功能。
gSetQuit()和gIsQuit()两个函数是设定全局退出信号变量的函数和检测退出信号的函数。SUN公司推荐,在有循环的地方,每次循环都用这种方式检测全局变量,在需要结束线程的地方设置全局变量,可以实现方便的多线程控制和同步问题。在对全局退出信号变量操作时,必须用互斥锁保护临界资源。
值得一提的是,在使用select函数时,每次循环前都要清空文件描述符集rfdset,才能保证每次循环开始时select函数正常检测文件IO 的变化。
我在程序中调试的经验是,select函数的阻塞时间可根据系统的实时响应需求来设定,比如一般的按键可接受在数百毫秒内完成响应,那么阻塞时间可以设置为100ms甚至更长。这个例子中,每次阻塞500ms后,若串口没有收到数据,则select返回0,并结束本次循环,判断gIsQuit()。
本文记述的线程退出方式安全稳定、程序逻辑设计简单,在许多场合,当需要程序员经常主动结束线程,并手动释放线程资源时,可以采用这种方式。
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、控制线程退出实例
pthread_mutex_t g_mutex; int g_flag_quit; static inline void gSetQuit(void) { pthread_mutex_lock(&g_mutex); g_flag_quit = 1; pthread_mutex_unlock(&g_mutex); } static inline int gIsQuit(void) { int quit; pthread_mutex_lock(&g_mutex); quit = g_flag_quit; pthread_mutex_unlock(&g_mutex); return quit; } void ThrFxn(void) { int serial_fd = open("/dev/ttyS0",O_WDRD); while(!gIsQuit()) { timeval timeout; fd_set rfdset; FD_ZERO(&rfdset); FD_SET(serial_fd, &rfdset); timeout.tv_sec = 0; timeout.tv_usec = 500000; rtn = select(1+fd_com1, &rfdset, NULL, NULL, &timeout); if(rtn == -1) { perror("select:error/n"); return; } else if(rtn == 0) { printf("thread time out!/n"); continue; } else { if (FD_ISSET(serial_fd, &rfdset)) { rtn = read(serial_fd, mbuffer, READ_SIZE); //读取数据后处理 } } } int main() { pthread_t pthr; int i=5; pthread_create(&pthr, NULL, ThrFxn, NULL); while(!IsQuit()) { if(i==0) { gSetQuit(); } sleep(1); printf("control thread quit %s sencons/n", i--); } }
这个例子中共有四个函数,主函数创建子线程,设定倒计时,控制子线程结束。子线程函数完成子线程的功能。
gSetQuit()和gIsQuit()两个函数是设定全局退出信号变量的函数和检测退出信号的函数。SUN公司推荐,在有循环的地方,每次循环都用这种方式检测全局变量,在需要结束线程的地方设置全局变量,可以实现方便的多线程控制和同步问题。在对全局退出信号变量操作时,必须用互斥锁保护临界资源。
值得一提的是,在使用select函数时,每次循环前都要清空文件描述符集rfdset,才能保证每次循环开始时select函数正常检测文件IO 的变化。
我在程序中调试的经验是,select函数的阻塞时间可根据系统的实时响应需求来设定,比如一般的按键可接受在数百毫秒内完成响应,那么阻塞时间可以设置为100ms甚至更长。这个例子中,每次阻塞500ms后,若串口没有收到数据,则select返回0,并结束本次循环,判断gIsQuit()。
本文记述的线程退出方式安全稳定、程序逻辑设计简单,在许多场合,当需要程序员经常主动结束线程,并手动释放线程资源时,可以采用这种方式。
相关文章推荐
- 应用select 函数控制多线程中子线程结束方法
- 应用select 函数控制多线程中子线程结束方法
- c#中跨线程调用windows窗体控件 .我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来做这个问题,下面我将详细的介绍。
- SE高阶(4):多线程(并发)—①创建启动方式和控制线程方法
- 多线程线程池控制一个方法的并发量 限制只有5个线程执行任务
- Tomcat中应用调用Libvirt库进行控制时可能导致线程卡死问题的解决方法
- python 多线程中子线程和主线程相互通信方法
- ros(robot operating system机器人操作系统)订阅函数的多线程使用方法(C++: 外部变量控制跳出for循环)
- 多线程中子线程控制进度条(参阅msdn的)
- C++类的静态成员函数在多线程的工作机制以及运行过程中强制结束线程实验
- c#中子线程控制进度条的一个简单例子(多线程问题)
- 多线程的使用方法、线程同步、线程状态及相应的一些线程函数用法、概述等。
- python 8-1 如何使用多线程,Thread创建线程,执行函数赋值给target//类+函数放在run方法中执行
- 【转载】Java多线程,判断其他线程是否结束的三种方法
- 线程超时等待方法---linux中select()函数使用
- java中结合单例模式,控制单例类的某方法在被多线程调用时,只被1个线程调用执行,执行完毕后才被另一线程调用
- 编写多线程代码时,启动线程后等待线程结束方法
- 多线程__【Thread类的方法--线程的调度与控制】
- C# 多线程的等待所有线程结束 用 ManualResetEvent 控制
- java中结合单例模式,控制单例类的某方法在被多线程调用时,只被1个线程调用执行,执行完毕后才被另一线程调用