| 一、 多线程概念 和多进程相比,多线程是一种比较节省资源的多任务操作方式。启动一个新的进程必须分配给它独立的地址空间,每个进程都有自己的堆栈段和数据段,系统开销比较高,进行数据的传递只能通过进程间通信的方式进行。在同一个进程中,可以运行多个线程,运行于同一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享全局变量和对象,启动一个线程所消耗的资源比启动一个进程所消耗的资源要少。 多线程可以共享资源(变量和对象),对编程带来了方便,但是某些对象虽然可以共享,但在同一个时间只能一个线程使用,多个线程同时使用会产生冲突,例如socket连接,数据库连接池。 二、相关API 1、创建线程 在Linux下,采用pthread_create函数来创建一个新的线程。 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
 参数thread为为指向线程标识符的地址。 参数attr用于设置线程属性,一般为空,表示使用默认属性。 参数start_routine是线程运行函数的地址,填函数名就可以了。 参数arg是线程运行函数的参数。新创建的线程从start_routine函数的地址开始运行,该函数只有一个无类型指针参数arg。 在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库。 2、线程的终止 如果进程中的任一线程调用了exit,则整个进程会终止,所以,在线程的start_routine函数中,不能采用exit。 线程的终止有三种方式: 1)线程的start_routine函数代码结束,自然消亡。 2)线程的start_routine函数调用pthread_exit结束。 3)被主进程或其它线程中止。 pthread_exit函数的声明如下: void pthread_exit(void *retval);
 参数retval填空,即0。 3、线程资源的回收 线程有joinable和unjoinable两种状态,如果线程是joinable状态,当线程主函数终止时(自己退出或调用pthread_exit退出)不会释放线程所占用内存资源和其它资源,这种线程被称为“僵尸线程”。创建线程时默认是非分离的,或者称为可连接的(joinable)。 避免僵尸线程就是如何正确的回收线程资源,有四种方法: 1)方法一(等待线程结束): 创建线程后,在主线程中调用pthread_join等待线程退出,类似于进程中wait/waitpid回收僵尸进程,一般不会采用这种方法,因为pthread_join会发生阻塞。 pthread_join(pthid,NULL);
 2)方法二: 创建线程前,调用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);
 3)方法三(分离线程->主): 创建线程后,在创建线程的程序中(主线程中)调用pthread_detach将新创建的线程设置为detached状态。   pthread_detach(pthid);
 4)方法四(分离线程->子): 在线程主函数中(子线程中)调用pthread_detach改变自己的状态。调用后和主线程分离,子线程结束时自己立即回收资源。   pthread_detach(pthread_self());
 4、查看线程 1)在top命令中,如果加上-H参数,top中的每一行显示的不是进程,而是一个线程。 top -H
 2)在ps命令中加-xH参数也可以显示线程,加grep可以过滤内容。 ps -xH
ps -xH|grep test
 三、使用多线程实现socket服务端 #include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
class CTcpServer
{
public:
  int m_listenfd;   
  int m_clientfd;   
 
  CTcpServer();
 
  bool InitServer(int port);  
 
  bool Accept();  
 
  
  int  Send(const void *buf,const int buflen);
  
  int  Recv(void *buf,const int buflen);
 
  
  
 
 ~CTcpServer();
};
 
CTcpServer TcpServer;
 
void EXIT(int sig)
{
  printf("程序退出,信号值=%d\n",sig);
 
  close(TcpServer.m_listenfd);  
 
  exit(0);
}
 
void *pth_main(void *arg);
 
int main()
{
  
  for (int ii=0;ii<50;ii++) signal(ii,SIG_IGN);
 
  
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
 
  if (TcpServer.InitServer(5051)==false)
  { printf("服务端初始化失败,程序退出。\n"); return -1; }
 
  while (1)
  {
    if (TcpServer.Accept() == false) continue;
 
    pthread_t pthid;   
    if (pthread_create(&pthid,NULL,pth_main,(void*)((long)TcpServer.m_clientfd))!=0)
    { printf("创建线程失败,程序退出。n"); return -1; }
 
    printf("与客户端通信的线程已创建。\n");
  }
}
 
CTcpServer::CTcpServer()
{
  
  m_listenfd=m_clientfd=0;
}
 
CTcpServer::~CTcpServer()
{
  if (m_listenfd!=0) close(m_listenfd);  
  if (m_clientfd!=0) close(m_clientfd);  
}
 
bool CTcpServer::InitServer(int port)
{
  if (m_listenfd!=0) { close(m_listenfd); m_listenfd=0; }
 
  m_listenfd = socket(AF_INET,SOCK_STREAM,0);  
 
  
  struct sockaddr_in servaddr;    
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  servaddr.sin_port = htons(port);  
  if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { close(m_listenfd); m_listenfd=0; return false; }
 
  
  if (listen(m_listenfd,5) != 0 ) { close(m_listenfd); m_listenfd=0; return false; }
 
  return true;
}
 
bool CTcpServer::Accept()
{
  if ( (m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;
 
  return true;
}
 
int CTcpServer::Send(const void *buf,const int buflen)
{
  return send(m_clientfd,buf,buflen,0);
}
 
int CTcpServer::Recv(void *buf,const int buflen)
{
  return recv(m_clientfd,buf,buflen,0);
}
 
void *pth_main(void *arg)
{
  int clientfd=(long) arg; 
 
  
  char strbuffer[1024];
 
  while (1)
  {
    memset(strbuffer,0,sizeof(strbuffer));
    if (recv(clientfd,strbuffer,sizeof(strbuffer),0)<=0) break;
    printf("接收:%s\n",strbuffer);
 
    strcpy(strbuffer,"ok");
    if (send(clientfd,strbuffer,strlen(strbuffer),0)<=0) break;
    printf("发送:%s\n",strbuffer);
  }
 
  printf("客户端已断开连接。\n");
 
  close(clientfd);  
 
  pthread_exit(0);
}
 需要注意几个问题: 1)线程主函数的函数体中,不能使用return;语句,如果想退出线程,可以用pthread_exit(0);返回。 2)线程可以共享全局变量,当然也可以共享TcpServer的m_clientfd成员变量,但是,创建线程的时候,为什么要把客户端的socket用参数传给线程主函数,而不是直接获取TcpServer.m_clientfd的值,因为主进程调用pthread_create创建线程后,立即返回循环重新Accept,创建线程需要时间,如果在这段时间内有新的客户端连接上来,TcpServer.m_clientfd的值会发生改变。 3)TcpServer.m_clientfd的强制转换,在创建线程的时候,代码如下: if (pthread_create(&pthid,NULL,pth_main,(void*)((long)TcpServer.m_clientfd))!=0)
 线程中的代码如下:   int clientfd=(long) arg; 
 四、 线程同步 线程同步是协调协同的意思 1、按预定的先后次序运行 2、公共资源同一时刻只能被一个线程使用,共享数据同一时刻只能被一个线程修改,以保证数据的完整性。 
 信号:类似进程间的信号处理 锁机制:互斥锁、读写锁、自旋锁 条件变量:使用通知的方式解锁linux 线程,与互斥锁配合使用 信号量:包括无名线程信号量和命名线程信号量 (编辑:南平站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |