1.线程介绍
在操作系统原理的术语中,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等,一个进程可以有很多线程,每条线程并行执行不同的任务。
典型的UNIX进程可以看作只有一个控制线程:一个进程在某一时刻只能做一件事情。有了多个控制线程后,在程序设计时就可以把进程设计成在某一时刻能够做不止一件事,每个线程处理各自独立的任务。
2.创建线程
1.一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),C/C++程序中,主线程就是通过main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。在创建多个线程以前,程序的行为与传统的进程并没有什么区别。新增的线程可以通过pthread_create函数创建。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
◆第三个参数start_routine是一个函数指针,它指向的函数原型是 void *func(void *),这是所创建的子线程要执行的任务(函数);
◆第四个参数arg就是传给了所调用的函数的参数,如果有多个参数需要传递给子线程则需要封装到一个结构体里传进去;
◆第一个参数thread是一个pthread_t类型的指针,他用来返回该线程的线程ID。每个线程都能够通过pthread_self()来获取自己的线程ID(pthread_t类型)。
◆第二个参数是线程的属性,其类型是pthread_attr_t类型,其定义如下:
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
设置这些属性可通过pthread_attr_init()等一系列线程属性设置函数进行设置。
2.无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:
- 可会合(joinable):这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
- 相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。
线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
3.代码实现
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#include <pthread.h>
#include <ctype.h>
#define MSG_NUM 1024
typedef void *(THREAD_BODY) (void *thread_arg);
void *thread_worker(void *ctx);
int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg);
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-p(--LISTEN_PORT): sepcify server port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main(int argc,char **argv)
{
int sockfd = -1;
int clifd ;
int rv =-1;
int LISTEN_PORT = 0;
int rw;
int on =1;
char buf[MSG_NUM];
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
pthread_t tid;
socklen_t cli_len=sizeof(struct sockaddr);
struct option longopts[] = {
{"help", no_argument, NULL, 'h'},
{"LISTEN_PORT", required_argument, NULL, 'P'},
{0, 0, 0, 0}
};
while( (rw=getopt_long(argc, argv, "p:h", longopts, NULL)) != -1 )
{
switch(rw)
{
case 'p':
LISTEN_PORT=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !LISTEN_PORT )
{
print_usage(argv[0]);
return 0;
}
sockfd=socket(AF_INET, SOCK_STREAM,0);
if(sockfd<0)
{
printf("Create socket failure:%s\n",strerror(errno));
return 0;
}
printf("Create socket[%d] successfully!\n",sockfd);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port = htons(LISTEN_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
{
printf("Bind socket failure:%s\n ",strerror(errno));
return -1;
}
printf("socket[%d] bind on port[%d] for all IP address ok",sockfd,LISTEN_PORT);
listen(sockfd,13);
printf("Start to listen on port [%d]\n", LISTEN_PORT);
while(1)
{
printf("Start accept new client incoming...\n");
printf("Value of cli_len:%d",cli_len);
clifd=accept(sockfd,(struct sockaddr*)&cliaddr,&cli_len);
if(clifd<0)
{
printf("Accept new client failure: %s\n", strerror(errno));
return -2;
}
printf("Accept new client[%s:%d] with fd [%d]\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port), clifd);
thread_start(&tid,thread_worker,(void *)clifd);
}
}
int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg)
{
int rv=-1;
pthread_attr_t thread_attr;
if( pthread_attr_init(&thread_attr) )
{
printf("pthread_attr_init() failure: %s\n", strerror(errno));
goto CleanUp;
}
if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
{
printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
goto CleanUp;
}
if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
{
printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
goto CleanUp;
}
if( pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg) )
{
printf("Create thread failure: %s\n", strerror(errno));
goto CleanUp;
}
rv=0;
CleanUp:
pthread_attr_destroy(&thread_attr);
return rv;
}
void *thread_worker(void *ctx)
{
int clifd;
int rv;
char buf[1024];
int i;
if( !ctx )
{
printf("Invalid input arguments in %s()\n", __FUNCTION__);
pthread_exit(NULL);
}
clifd = (int)ctx;
printf("Child thread start to commuicate with socket client...\n");
while(1)
{
memset(buf, 0, sizeof(buf));
rv=read(clifd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from client[%d] failure:%s\n",clifd,strerror(errno));
close(clifd);
pthread_exit(NULL);
}
if (rv==0)
{
printf("The client[%d] has disconnected!",clifd);
pthread_exit(NULL);
}
for(i=0; i<rv; i++)
{
buf[i]=toupper(buf[i]);
}
printf("Read %d bytes data from socket[%d]:%s\n",rv,clifd,buf);
if((write(clifd,buf,rv))<0)
{
printf("write to client[%d] failure:%s\n",clifd,strerror(errno));
close(clifd);
pthread_exit(NULL);
close(clifd);
pthread_exit(NULL);
}
}
}