Linux多线程详解

P2.多线程基本知识

  1. 查看线程:ps -xH|grep 进程名 ;查看进程:ps -ef |grep 进程名
    在这里插入图片描述
  2. main函数为主进程/主线程,创建的线程为子线程
  3. 线程资源是共享的,使用相同的地址共享全局变量和对象
  4. 不能在子线程中使用exit,否则整个进程会退出,一般使用pthread_exit(0)
  5. 子线程退出尽量不要使用return,否则会报错,可以写为 return (void *)0;
    代码示例:
#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
int main()
 { 
   pthread_t pthid1,pthid2;
  if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  { 
    printf("创建线程pthid1失败\n");
    return -1;
  }
  if(pthread_create(&pthid2,NULL,handler2,NULL)!=0)
  { 
    printf("创建线程pthid2失败\n");
    return -1;
  }
   printf("pthid1=%lu,pthid2=%lu\n",pthid1,pthid2);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   pthread_join(pthid1,NULL);
   printf("子线程1已退出\n");
   pthread_join(pthid2,NULL);
   printf("子线程2已退出\n");
   return 0;
}
void* handler1(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       ++var;
       printf("handler1 sleep 1sec ok,%d\n",var);
    }
   //子线程退出
   pthread_exit(0);
}
void* handler2(void* arg)
{
  for(int i=0;i<30;i++)
    {
       sleep(1);
       printf("handler2 sleep 1sec ok,%d\n",var);
    }
}

运行窗口
在这里插入图片描述

P3.线程参数的传递

  1. 不能使用全局变量传参,因为线程跑起来,全局变量可能已经被改变了
  2. 数据类型的强制转换,用于线程的参数:指针8字节,int是4字节
    int i=10; void* p=(void*)(long)i;
    int jj=(int)(long)p;
  3. 二级指针概念:
    在这里插入图片描述
    代码示例
#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
void* handler3(void* arg);
void* handler4(void* arg);
void* handler5(void* arg);
long var=0;
int main()
 {
   pthread_t pthid1,pthid2,pthid3,pthid4,pthid5;
  if(pthread_create(&pthid1,NULL,handler1,(void*)var)!=0)
  {
    printf("创建线程pthid1失败\n");
    return -1;
  }
  var++;
  if(pthread_create(&pthid2,NULL,handler2,(void*)var)!=0)
  {
    printf("创建线程pthid2失败\n");
    return -1;
  }
  var++;
  if(pthread_create(&pthid3,NULL,handler3,(void*)var)!=0)
  {
    printf("创建线程pthid3失败\n");
    return -1;
  }
  var++;
  if(pthread_create(&pthid4,NULL,handler4,(void*)var)!=0)
  {
    printf("创建线程pthid4失败\n");
    return -1;
  }
  var++;
  if(pthread_create(&pthid5,NULL,handler5,(void*)var)!=0)
  {
    printf("创建线程pthid5失败\n");
    return -1;
  }
   printf("pthid1=%lu,pthid2=%lu,pthid3=%lu,pthid4=%lu,pthid5=%lu\n",pthid1,pthid2,pthid3,pthid4,pthid5);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   pthread_join(pthid1,NULL);
   printf("子线程1已退出\n");
   pthread_join(pthid2,NULL);
   printf("子线程2已退出\n");
   pthread_join(pthid3,NULL);
   printf("子线程3已退出\n");
   pthread_join(pthid4,NULL);
   printf("子线程4已退出\n");
   pthread_join(pthid5,NULL);
   printf("子线程5已退出\n");
   return 0;
}
void* handler1(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler1 sleep 1sec ok,%d\n",(int)(long)arg);
    }
   //子线程退出
   pthread_exit(0);
}
void* handler2(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler2 sleep 1sec ok,%d\n",(int)(long)arg);
    }
   pthread_exit(0);
}
void* handler3(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler3 sleep 1sec ok,%d\n",(int)(long)arg);
    }
   pthread_exit(0);
}
void* handler4(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler4 sleep 1sec ok,%d\n",(int)(long)arg);
    }
   pthread_exit(0);
}
void* handler5(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler5 sleep 1sec ok,%d\n",(int)(long)arg);
    }
   pthread_exit(0);
}

运行窗口
在这里插入图片描述

P4.线程资源的回收

  1. 线程有两种状态:joinable,unjoinable
    joinable:当子线程主函数终止不会释放线程所占用的资源,线程为僵尸线 程,创建线程默认属性为joinable
  2. pthread_join会在主进程中阻塞线程,一般不会使用
  3. 创建线程前,调用pthread_attr_setdetachstate将线程设为detached,这 样线程退出时,系统自动回收线程资源。
  4. 分离前pthread_join返回值为0,分离后返回值为errror=22,Invalid argument
    在这里插入图片描述
  5. 创建线程后调用pthread_detach将新的线程设置为detach状态
  6. 在线程主调函数里面调用pthread_detach(pthread_self())
    代码示例
#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
 {
   pthread_t pthid1,pthid2;
  /* pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);  // 设置线程的属性。*/
  if(pthread_create(&pthid1,NULL,handler1,NULL)!=0) {
    printf("创建线程pthid1失败\n");
    return -1;
  } 
  //pthread_detach(pthid1);
  if(pthread_create(&pthid2,NULL,handler2,NULL)!=0)
  {
    printf("创建线程pthid2失败\n");
    return -1;
  } 
  //pthread_detach(pthid2);
   printf("pthid1=%lu,pthid2=%lu\n",pthid1,pthid2);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   int ret=pthread_join(pthid1,NULL);
   printf("子线程1已退出:%d,%s\n",ret,strerror(ret));
  // pthread_join(pthid2,NULL);
   printf("子线程2已退出\n");
   sleep(10);
   return 0;
}  
void* handler1(void* arg)
{
  pthread_detach(pthread_self());
  for(int i=0;i<5;i++)
    {
       sleep(1);
       ++var;
       printf("handler1 sleep 1sec ok,%d\n",var);
    }
   //子线程退出
   pthread_exit(0);

}
void* handler2(void* arg)
{
  pthread_detach(pthread_self());
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler2 sleep 1sec ok,%d\n",var);
    }
    pthread_exit(0);
}

运行窗口
在这里插入图片描述

P5.线程返回的状态

  1. int pthread_join(pthread_t thread, void **retval);
    通过参数retval接受线程的返回值
    根据线程返回值,来判断线程的状态
    代码示例
#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
int main()
 {
   pthread_t pthid1;
   if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  {
    printf("创建线程pthid1失败\n");
    return -1;
  }
   printf("pthid1=%lu\n",pthid1);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   int ret;
   int result=pthread_join(pthid1,(void** )&ret);
   printf("子线程1已退出(result=%d,ret=%d)\n",result,ret);
   return 0;
}
void* handler1(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       ++var;
       printf("handler1 sleep 1sec ok,%d\n",var);
    }
   // return (void*)1;
   //子线程退出
   pthread_exit(0);
}

运行窗口
在这里插入图片描述

P6.线程的取消

  1. int pthread_cancel(pthread_t thread);

  2. 调用线程取消函数,线程返回值为-1;

  3. int pthread_setcancelstate(int state, int *oldstate);
    其中state有两种状态:
    PTHREAD_CANCEL_ENABLE(缺省情况)
    PTHREAD_CANCEL_DISABLE

  4. 取消点(Cancellation points):会产生I/O等待的函数
    在这里插入图片描述

  5. 取消点设置函数:void pthread_testcancel(void);

  6. 取消类型有两种:延迟取消,立即取消
    int pthread_setcanceltype(int type, int *oldtype);
    PTHREAD_CANCEL_DEFERRED(延迟)
    PTHREAD_CANCEL_ASYNCHRONOUS(立即)
    代码部分(立即取消)

  7. 应用于总线程获取多个线程的状态,一旦某个线程挂掉,总线程即cancel子线程

#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
int main()
 {
   pthread_t pthid1;
   if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  {
    printf("创建线程pthid1失败\n");
    return -1;
  }
   usleep(2);
   pthread_cancel(pthid1); printf("pthid1=%lu\n",pthid1);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   int ret;
   int result=pthread_join(pthid1,(void** )&ret);
   printf("子线程1已退出(result=%d,ret=%d)\n",result,ret);
   return 0;
}
void* handler1(void* arg)
{

 // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
   //pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
   int jj=0;
  for(int i=0;i<5000000;i++)
    {
       ;
       jj++;
       jj--;
       jj++;
      //sleep为延迟取消点
       //sleep(1);
       ++var;
    //设置取消点,
     // pthread_testcancel();
     //printf("handler1 sleep 1sec ok,%d\n",var);
    }
    printf("jj=%d\n",jj);
   // return (void*)1;
   //子线程退出
   pthread_exit(0);
}

运行窗口
在这里插入图片描述
代码部分(延迟取消)

#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
int main()
 {
   pthread_t pthid1;
   if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  {
    printf("创建线程pthid1失败\n");
    return -1;
  }
   usleep(2);
   pthread_cancel(pthid1); printf("pthid1=%lu\n",pthid1);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   int ret;
   int result=pthread_join(pthid1,(void** )&ret);
   printf("子线程1已退出(result=%d,ret=%d)\n",result,ret);
   return 0;
}
void* handler1(void* arg)
{
 // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
  // pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
   int jj=0;
  for(int i=0;i<5000000;i++)
    {
       ;
       jj++;
       jj--;
       jj++;
      //sleep为延迟取消点
       //sleep(1);
       ++var;
    //设置取消点,
     // pthread_testcancel();
     //printf("handler1 sleep 1sec ok,%d\n",var);
    }
    printf("jj=%d\n",jj);
   // return (void*)1;
   //子线程退出
   pthread_exit(0);
}

运行窗口
在这里插入图片描述

P7.线程清理

  1. 子线程退出时可能需要执行善后工作,如释放资源和锁,回滚事务等

  2. 善后代码写在清理函数中,不写在主线程

  3. 清理函数定义:void pthread_cleanup_push(void (*routine)(void *),
    void *arg);在清理函数中关闭socket,将socket传给arg,将该socket进行关闭

  4. void pthread_cleanup_pop(int execute);当execute=0时,清理函数不被执行,execute=1清理函数被执行

  5. 当执行线程取消函数,会弹出清理函数,并且执行它

  6. 当执行pthread_exit(0),也会弹出清理函数,并且执行它
    示例代码

#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;

void cleanfun1(void*);
void cleanfun2(void*);
void cleanfun3(void*);
int main()
 { 
   pthread_t pthid1;
   if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  { 
    printf("创建线程pthid1失败\n");
    return -1;
  }
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   sleep(2);
   pthread_cancel(pthid1);
   int ret;
   int result=pthread_join(pthid1,(void** )&ret);
   printf("子线程已退出(result=%d,ret=%d)\n",result,ret);
   return 0;
}
void* handler1(void* arg)
{
  // 注册线程清理函数
  int socketid=10;
  pthread_cleanup_push(cleanfun1,(void*)(long)socketid);
  pthread_cleanup_push(cleanfun2,NULL);
  pthread_cleanup_push(cleanfun3,NULL);
  for(int i=0;i<3;i++)
  {
     sleep(1);printf("sleep %dsec ok.\n",i+1);
  }
  //pthread_exit(0);

  pthread_cleanup_pop(1);
  pthread_cleanup_pop(1);
  pthread_cleanup_pop(1);
   pthread_exit(0);
}
void cleanfun1(void* arg )
{
  printf("fun1 is ok,arg=%d\n",(int)(long)arg);
}
void cleanfun2(void*)
{
  printf("fun2 is ok\n");
}
void cleanfun3(void*)
{
  printf("fun3 is ok\n");
}

运行窗口
在这里插入图片描述

P8.线程与信号

  1. 外部使用killl命令产生的信号,通常是SIGINT等控制信号,则会遍历所有线程,直到找到一个不阻塞的线程,然后调用处理(一般从主线程找起)
  2. 信号并不会中断线程,主进程,线程继续处理当信号到来时
  3. 多个线程程序中,在某一个线程中调用signal会改变所有线程中的信号处理函数
  4. 主进程向子线程发送信号用pthread_kill函数,当信号处理函数和和其他线程执行完,才会继续执行中断的线程
    在这里插入图片描述
  5. 不存在对不同的线程设置不同处理函数的说法,正确的做法:设置一个公共的信号处理函数,并利用pthread_kill(pthread,2)对不同的信号进行中断,如果设置没有设置信号处理函数,则整个函数退出。
    代码示例
void* handler1(void* arg);
void* handler2(void* arg);
int var=0;
void hanfunc(int arg);
 {
  // signal(2,hanfunc);
   signal(2,hanfunc);
   pthread_t pthid1,pthid2;
  if(pthread_create(&pthid1,NULL,handler1,NULL)!=0)
  {
    printf("创建线程pthid1失败\n");
    return -1;
  } 
  if(pthread_create(&pthid2,NULL,handler2,NULL)!=0)
  {
    printf("创建线程pthid2失败\n");
    return -1;
  } 
   sleep(1),pthread_kill(pthid2,2);
   printf("等待子线程退出\n");
   //pthread_join会使主进程阻塞在这里,用于调试
   int ret,ists;
   ret=pthread_join(pthid1,NULL);
   ret=pthread_join(pthid2,NULL);
   printf("子线程1已退出\n");
   return 0;
}  
void* handler1(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler1 sleep 1sec ok,%d\n",i);
    }
   //子线程退出
   pthread_exit(0);
}
void* handler2(void* arg)
{
  for(int i=0;i<5;i++)
    {
       sleep(1);
       printf("handler2 sleep 1sec ok,%d\n",i);
    }
   //子线程退出
   pthread_exit(0);
}
void hanfunc(int arg)
{
  printf("sig=%d\n",arg);
  for(int j=1;j<4;j++){
       printf("hanfunc j(%d)=%d\n",arg,j);
       sleep(1);
   }
}
  

运行窗口
在这里插入图片描述

P9.多线程程序的调试

  1. 查看当前运行的轻量级进程:ps -aL |grep 进程名
  2. 查看主进程和新进程的关系:pstree -p 主线程id
    在这里插入图片描述
  3. info b查看断点信息
  4. info pthreads:查看当前有多少线程
    在这里插入图片描述
  5. thread ID 切换到线程ID
  6. 只运行当前线程 set scheduler-locking on,其他线程挂起
  7. 线程全部运行,set scheduler-locking off
  8. 指定某线程执行某gdb命令:thread apply 线程id cmd
    thread apply all cmd
    多线程调试代码示例
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>

void* pth1_main(void* arg);
void* pth2_main(void* arg);
int i=0;
int j=0;
int main()
{  
   pthread_t pthid1,pthid2;
   if(pthread_create(&pthid1,NULL,pth1_main,(void*)0)!=0)
   {  
      printf("pthread1_create fail\n");
      return -1;
   }
   if(pthread_create(&pthid2,NULL,pth2_main,(void*)0)!=0)
   {  
      printf("pthread2_create fail\n");
      return -1;
   }
   printf("111\n");
   pthread_join(pthid1,NULL);
   printf("222\n");
   pthread_join(pthid2,NULL);
   return 0;
}
void* pth1_main(void* arg)
{
    for( ;i<100;i++)
    {
          printf("i=%d\n",i);
          sleep(1);
    }
    pthread_exit(NULL);
}
void* pth2_main(void* arg)
{
    for(;j<100;j++)
    {
          printf("j=%d\n",j);
          sleep(1);
    }
    pthread_exit(NULL);

}

P10-11.学习多线程网路服务端前的准备

  1. 熟悉socket通信
  2. 熟悉信号
  3. 熟悉多进程
  4. 了解freecplus框架

P12.搭建多线程的网络服务端框架

  1. 让线程去和客户端进程通信,主进程继续负责监听
  2. 不能使用全局变量socket,因为每连接上一个客户端,socket值会变
    因此需要将socket参数在创建进程的时候就传进去。
    在这里插入图片描述在这里插入图片描述
    服务端代码框架
#include "../_freecplus.h"
void* pthmain(void* arg);
{
  CTcpServer TcpServer;   // 创建服务端对象。
  
  if (TcpServer.InitServer(5005)==false) // 初始化TcpServer的通信端口。
  {
    printf("TcpServer.InitServer(5858) failed.\n"); return -1;
  } 
 while(true)
 { 
  if (TcpServer.Accept()==false)   // 等待客户端连接。
  {
    printf("TcpServer.Accept() failed.\n"); return -1;
  } 
  printf("客户端(%s)已连接。\n",TcpServer.GetIP());
  pthread_t pthid1;
  if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0)
 {
  }
 while(true)
 { 
  if (TcpServer.Accept()==false)   // 等待客户端连接。
  { 
    printf("TcpServer.Accept() failed.\n"); return -1;
  }
  printf("客户端(%s)已连接。\n",TcpServer.GetIP());
  pthread_t pthid1;
  if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0)
  {  
  {
  { 
     printf("creat pthid1 fail\n");
     return -1;
  }
    printf("TcpServer.InitServer(5858) failed.\n"); return -1;
  }
 while(true)
 {
  if (TcpServer.Accept()==false)   // 等待客户端连接。
  { 
    printf("TcpServer.Accept() failed.\n"); return -1;
  }
  printf("客户端(%s)已连接。\n",TcpServer.GetIP());
  pthread_t pthid1;
  if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0)
  {  
     printf("creat pthid1 fail\n");
     return -1;
  }
  { 
    printf("TcpServer.Accept() failed.\n"); return -1;
  }
  printf("客户端(%s)已连接。\n",TcpServer.GetIP());
  pthread_t pthid1;
  if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0)
  {  
     printf("creat pthid1 fail\n");
     return -1;
  }
 }
   return 0;
}  
void* pthmain(void* arg)
{
   int sockfd=(int)(long)(arg);
  while (true)
  { 
    char strbuffer[1024];  // 存放数据的缓冲区。
    int buflen=0;
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpRead(sockfd,strbuffer,&buflen,300)==false) break; // 接收客户端发过来的请求报文。
    printf("接收:%s\n",strbuffer);

    strcat(strbuffer,"ok");      // 在客户端的报文后加上"ok"。
    printf("发送:%s\n",strbuffer);
    if (TcpWrite(sockfd,strbuffer,buflen)==false) break;     // 向客户端回应报文。
  }
  printf("客户端已断开。\n");
}

客户端代码框架

#include "../_freecplus.h"
int main(int argc,char *argv[])
{
  CTcpClient TcpClient;   // 创建客户端的对象。
  if (TcpClient.ConnectToServer("192.168.74.10",5005)==false) // 向服务端发起连接请求。
  {
    printf("TcpClient.ConnectToServer(\"172.21.0.3\",5858) failed.\n"); return -1;
  }
  char strbuffer[1024];    // 存放数据的缓冲区。
  for (int ii=0;ii<5;ii++)   // 利用循环,与服务端进行5次交互。
  {
    memset(strbuffer,0,sizeof(strbuffer));
    snprintf(strbuffer,50,"(%d)这是第%d个超级女生,编号%03d。",getpid(),ii+1,ii+1);
    printf("发送:%s\n",strbuffer);
    if (TcpClient.Write(strbuffer)==false) break;    // 向服务端发送请求报文。
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpClient.Read(strbuffer,20)==false) break;  // 接收服务端的回应报文。
    printf("接收:%s\n",strbuffer);
    sleep(1);
  }
  // 程序直接退出,析构函数会释放资源。
}

运行窗口
在这里插入图片描述

P13.多线程服务程序的退出

思路

  1. 在主进程中设置程序退出的信号2 和15的信号处理函数
  2. 在信号处理函数中取消主进程的监听,设置全部的线程取消
  3. 在线程处理函数中首先注册线程清理函数,将线程设置为分离,将取消类型设置为(立即取消、延迟取消),并且最后执行线程清理函数(注册清理函数和执行函数成对出现)
  4. 编写清理函数,将线程连接的socket关闭
    在这里插入图片描述
    需要注意的点:收到2,15信号,执行信号函数取消同样会调用线程清理函数,我这里usleep(200),即可发现这个问题

服务端代码

#include "../_freecplus.h"
void* pthmain(void* arg);
void mainexit(int sig);
vector<pthread_t>vpthid;
CTcpServer TcpServer;   // 创建服务端对象。
int main(int argc,char *argv[])
{ 
  signal(2,mainexit);signal(5,mainexit); 
  if (TcpServer.InitServer(5005)==false) // 初始化TcpServer的通信端口。
  { 
    printf("TcpServer.InitServer(5858) failed.\n"); return -1;
  }
 while(true)
 { 
  if (TcpServer.Accept()==false)   // 等待客户端连接。
  { 
    printf("TcpServer.Accept() failed.\n"); return -1;
  }
  printf("客户端(%s)已连接。\n",TcpServer.GetIP());
  pthread_t pthid1;
  if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0)
  {
     printf("creat pthid1 fail\n");
     return -1;
  }
  vpthid.push_back(pthid1);
 }
   return 0;
}
//2,15信号处理函数
void mainexit(int sig)
{
  printf("mainexit begin.\n");
  TcpServer.CloseListen();//关闭监听的socket
  for(int i=0;i<vpthid.size();i++)
  {
      printf("cancel %ld\n",vpthid[i]);
      pthread_cancel(vpthid[i]);//取消全部的线程
  }
  printf("mainexit end.\n");
  usleep(20);
  exit(0);
}
//设置线程清理函数
void pthmainexit(void* arg)
{
   printf("线程清理函数开始。\n");
  close((int)(long)arg);//关闭线程连接的socket 
  for(int i=0;i<vpthid.size();i++)
  {
    if(vpthid[i]==pthread_self())
     {
       vpthid.erase(vpthid.begin()+i);
     }
  }
  printf("线程清理函数结束.\n");
}
void* pthmain(void* arg)
{
  pthread_cleanup_push(pthmainexit,arg);//注册线程清理函数
  pthread_detach(pthread_self());
  pthread_setcanceltype(PTHREAD_CANCEL_DISABLE,NULL);
  int sockfd=(int)(long)(arg);
  while (true)
  {
    char strbuffer[1024];  // 存放数据的缓冲区。
    int buflen=0;
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpRead(sockfd,strbuffer,&buflen,300)==false) break; // 接收客户端发过来的请求报文。
    printf("接收:%s\n",strbuffer);

    strcat(strbuffer,"ok");      // 在客户端的报文后加上"ok"。
    printf("发送:%s\n",strbuffer);
    if (TcpWrite(sockfd,strbuffer,buflen)==false) break;     // 向客户端回应报文。
  }
  printf("客户端已断开。\n");
  pthread_cleanup_pop(1);//执行线程清理函数
}

运行窗口
在这里插入图片描述

  • 18
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex1_Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值