Linux多线程 | pthread

相关函数

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");

}

  1. 一般不采用,因为会发生阻塞 ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值