1、 线程介绍
在操作系统原理的术语中,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境 (register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。
2、线程的创建
一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),C/C++程序中,主线程就是通过 main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户 在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊 之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。
无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有 子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁 后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资 源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销 毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:
1. 可会合(joinable):
这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线 程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函 数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否 则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
2. 相分离(detached):
表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种 方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是 很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。
线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程 等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其 他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
3、多线程服务器编程代码:
#ifndef __SOCKET_SERVER_H //防止重重定义
#define __SOCKET_SERVER_H
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include<stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
#include <ctype.h>
#define MSG_STR "Hello network word! I'm sever!"
#define BUF_SIZE 1024
#endif
typedef void *(WORKWER_BODY) (void *thread_arg); //定义函数指针
void *thread_worker(void *ctx);
int thread_start(pthread_t * thraed_id, WORKWER_BODY* thread_workbody, void * thread_arg);
void print_usage(char *prograname)
{
printf("%s usage : \n", prograname);
printf("-p(--port): specify sever listen port.\n");
printf("-h(--help): print this help information.\n");
return ;
}
int main (int argc, char **argv)
{
int sockfd;
int rv = -1;
int clifd;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int port = 0;
int ch;
int on = 1;
pthread_t tid;
struct option opts[] = {
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while((ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 ) //实现参数解析
{
switch(ch)
{
case 'p':
port=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !port )
{
print_usage(argv[0]);
return 0;
}
sockfd = socket(AF_INET,SOCK_STREAM,0); //服务器第一步,socket();
if(sockfd < 0)
{
printf("Cearte socket failure :%s\a\n",strerror(errno));
return -1;
}
printf("Create socket[%d] sucessfully.\n",sockfd);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on,sizeof(on));//使得端口号在短时间内可以得到调用
memset(&servaddr,0,sizeof(servaddr)); //清空servaddr结构体
servaddr.sin_family = AF_INET; //设置ipv4协议,如果要设置ipv6协议, 则需AF_INET6;
servaddr.sin_port = htons(port); //将本地字节序的端口转化为网络字节序
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //可以监听任何访问IP地址
rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); //服务器第二步,bind();
if(rv < 0 )
{
printf(" socket[%d] bund port[%d] failure :%s\n\a",sockfd, port,strerror(errno));
return -2;
}
listen(sockfd,13); //服务器第三步,listen;
printf("Statring to listen on port[%d].\n",port);
while(1)
{
printf("Starting to accept new client incomming...\n");
clifd = accept(sockfd, (struct sockaddr *)&cliaddr,&len); //服务器第四步,accept(),本处会从在阻塞
if(clifd < 0)
{
printf("Accept new client failure :%s\n\a", strerror(errno));
}
printf("Accept new client[%s:%d]successfuly\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
thread_start(&tid, thread_worker,(void *)(long)clifd ); //创建新的线程来解决accept阻塞问题
}
close(sockfd);
return 0;
}
int thread_start(pthread_t * thread_id, WORKWER_BODY* thread_workbody, void * thread_arg)
{
int rv = -1;
pthread_attr_t thread_attr;
if(pthread_attr_init(&thread_attr)) //初始化线程属性 thread_attr
{
printf("pthread_attr_init() failure:%s\n", strerror(errno));
goto CleadUp;
}
if(pthread_attr_setstacksize(&thread_attr, 12*1024)) //设置栈大小
{
printf("pthread_attr_setstacksize() failure:%s\n", strerror(errno));
goto CleadUp;
}
if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED)) //设置可分离的状态
{
printf("pthread_attr_detachstate() failure:%s\n", strerror(errno));
goto CleadUp;
}
if(pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg)) //创建子线程
{
printf("pthread_create() failure:%s\n", strerror(errno));
goto CleadUp;
}
rv = 0;
CleadUp:
pthread_attr_destroy(&thread_attr); //摧毁线程属性,释放占用资源
return rv;
}
void *thread_worker( void *ctx) //服务器第五步 read()会阻塞, write();
{
int clifd = 0;
char buf[1024];
int rv = 0;
if(!ctx)
{
printf("Invalid input arguements in %s()\n", __FUNCTION__);
pthread_exit(NULL);
}
clifd = (long)ctx;
printf("clifd = %d\n",clifd );
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 from client socket[%d] failure: %s\n",clifd,strerror(errno));
close(clifd);
pthread_exit(NULL);
}
else if(rv == 0)
{
printf("socket[%d] get disconnected\n",clifd);
close(clifd);
pthread_exit(NULL); //子线程退出必须调用pthread_exit(),调用exit()或return 都会使父子线程都结束
}
else if(rv >0)
{
printf("Read %d bytes data form client:\n%s\n",rv,buf);
}
rv = write(clifd,MSG_STR,strlen(MSG_STR));
if(rv < 0)
{
printf("write to client by socket[%d] failure :%s.\a\n",clifd,strerror(errno));
close(clifd);
pthread_exit(NULL);
}
}
}