相关函数
1、线程创建函数 pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
- @@thread:
线程标识符 - @attr:
线程属性,传递NULL为默认属性 - @start_routine:
建立的新线程的函数指针 - @arg:
线程的参数(可用于传递socket) - return:
成功返回0,错误返回errno,且thread不定义
EAGAIN:资源不足创建新线程
EINVAL:输入无效的线程属性
EPERM:无权限设置调度策略和线程属性参数即attr
2、线程终止 pthread_cancel | pthread_exit
int pthread_cancel(pthread_t thread);
- @thread: 需要终止的线程标识号
- return:成功返回0 错误返回errno
默认情况下,使用cancel函数后,线程并不会立刻终止。取决于一下两个属性:线程终止状态 cancelstate、线程终止类型 canceltype。 见下两个函数:
void pthread_exit(void *retval);
- 线程的start_routine函数调用pthread_exit结束。
注意:线程主函数的函数体中,不能使用return;语句,如果想退出线程,可以用pthread_exit(0);返回。
3、设置线程终止状态 pthread_setcancelstate
int pthread_setcancelstate(int state,int *oldstate);
- @state:
1、PTHREAD_CANCEL_ENABLE: 线程对cancel信号立即有反应,将设置为CANCEL状态 (默认)
2、PTHREAD_CANCEL_DISABLE:如果线程state为不可取消,线程不理会信号,继续执行,而使用cancel函数的线程会一直阻塞到可取消状态 - @oldstate:
1、NULL:state写入有效,即当前只想设置属性,而不关心原来的属性
2、不为NULL:state不写入,保持原有的设定,且获取原来的属性 - return:成功返回0,错误返回errno
4、设置线程终止类型
int pthread_setcanceltype(int type,int *oldtype)
-
@type:
1、PTHREAD_CANCEL_DEFERRED:
运行到下一个取消点就退出 (默认)
2、PTHREAD_CANCEL_ASYNCHRONOUS:
直接退出 -
@oldtyoe:
1、NULL:type写入有效,即当前只想设置属性,而不关心原来的属性
2、不为NULL:type不写入,保持原有的设定,且获取原来的属性 -
return:
成功返回0,错误返回errno
EINVAL:无效的type
5、 线程资源的回收
线程有joinable和unjoinable两种状态,如果线程是joinable状态,当线程主函数终止时(自己退出或调用pthread_exit退出)不会释放线程所占用内存资源和其它资源,这种线程被称为“僵尸线程”。创建线程时默认是非分离的,或者称为可连接的(joinable)。
避免僵尸线程就是如何正确的回收线程资源,有四种方法:
pthread_join1
- 创建线程后,在创建线程的程序中调用pthread_join等待线程退出
pthread_join(pthid,NULL);
pthread_attr_setdetachstate
- 创建线程前,调用pthread_attr_setdetachstate将线程设为detached,这样线程退出时,系统自动回收线程资源。
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // 设置线程的属性。
pthread_create(&pthid,&attr,pth_main,(void*)((long)TcpServer.m_clientfd);
pthread_detach
- 创建线程后,在创建线程的程序中调用pthread_detach将新创建的线程设置为detached状态。
pthread_detach(pthid);
- 在线程主函数中调用pthread_detach改变自己的状态。
pthread_detach(pthread_self());
6、线程清理
子线程退出时可能需要执行善后的工作,如释放资源和锁、回滚事务等。善后的代码不合适写在线程主函数中,一般放在清理函数中。
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
void pthread cleanup _ push ( void (* routine )( void *), void * arg ); //挂接清理函数
void pthread cleanup _ pop ( int execute ); //触发清理函数
-
@routine:清理函数函数指针。
-
@arg:传给清理函数的 Void pointer(可指向任何类型的数据)。
-
@execute:为0弹出不执行清理函数,非0弹出并执行清理函数。
-
清理函数必须成对的书写在同一语句块中。
-
当线程被取消时,所有注册的清理函数以被推送到堆栈的顺序相反的顺序执行。当线程通过调用 pthread _ exit 终止时,所有清理处理程序都将按照前一点所述执行。(如果线程通过 return 终止,则不调用清理函数。)
-
当线程使用非零的 execute 参数调用 pthread _ cleanup _ pop 时,将弹出并执行最上面的清理函数。
多线程程序的退出
- 设置程序退出信号
- 收到信号后调用pthread_cancel取消子进程,释放全局资源,然后退出;
- 子进程采用清理函数释放资源
- 子线程为分离状态
- 子线程的取消方式为立即取消
代码
例子来自b站:码农有道,并使用其cfreeplus框架
#include"_freecplus.h"
void *pthmain(void *arg); //pthread main function
CTcpServer TcpServer; //create server
vector<long> vpthid; //storage pthread ids
void mainexit(int sig); //signal processing
void pthmainexit(void *arg); //pthread clean function
CLogFile logfile;
int main()
{
signal(2, mainexit);
signal(15, mainexit); //catch signal 2&5
logfile.Open("/tmp/mutithread.log", "a+");
if(TcpServer.InitServer(5005) == false) //init TcpServer communication port
{
logfile.Write("TcpServer.InitServer(5005) failed.\n");
return -1;
}
while(true)
{
if(TcpServer.Accept() == false) //wait for the client to connection
{
logfile.Write("TcpServer.Accept() failed.\n");
return -1;
}
logfile.Write("The Client(%s) is connected.\n", TcpServer.GetIP());
pthread_t pthid; //After connection, use multithreading to process services
if(pthread_create(&pthid, NULL, pthmain, (void *)(long)TcpServer.m_connfd) != 0)
{
logfile.Write("pthread_create falid.\n");
return -1;
}
vpthid.push_back(pthid);
}
return 0;
}
void *pthmain(void *arg)
{
pthread_cleanup_push(pthmainexit,arg); //set thread clean function
pthread_detach(pthread_self()); //detach thread
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //set canceltype to immediate cancel
int sockfd = (int)(long)arg;
int ibuflen = 0;
char strbuffer[1024];
while(true)
{
bzero(strbuffer, sizeof(strbuffer));
if(TcpRead(sockfd, strbuffer, &ibuflen, 300) == false) break; //receive request message from client
logfile.Write("receive: %s\n", strbuffer);
strcat(strbuffer,"ok");
logfile.Write("send: %s\n", strbuffer);
if(TcpWrite(sockfd, strbuffer) == false) break; //return to the client
}
logfile.Write("Client disconnect!\n");
pthread_cleanup_pop(1);
pthread_exit(0);
}
void mainexit(int sig)
{
logfile.Write("mainexit begin.\n");
TcpServer.CloseListen();
// cout << "BBBB\n";
for(int i = 0; i < vpthid.size(); i++)
{
// cout << "aaa\n";
logfile.Write("cancel %ld\n", vpthid[i]);
pthread_cancel(vpthid[i]);
}
logfile.Write("mainexit end.\n");
exit(0);
}
void pthmainexit(void *arg)
{
logfile.Write("pthmainexit begin.\n");
close((int)(long)arg);
//delete this thread id from vpthid
for(int i = 0; i < vpthid.size(); i++)
{
if(vpthid[i] == pthread_self())
{
vpthid.erase(vpthid.begin()+i);
}
}
logfile.Write("pthmainexit end.\n");
}
一般不采用,因为会发生阻塞 ↩︎