Linux之并发多线程服务器-2

从上面我们可以实现多线程服务器,但是我们在实现复杂功能时,要注意避免使用静态变量和同步问题。Linux系统中提供了线程特定数据(TSD)来取代静态变量。它类似于全局变量。但是是各个线程私有的,它以线程为界限。TSD是定义线程私有数据的唯一方法。同一进程中的所有线程,他们的同一特定数据项都由一个进程内唯一的关键字 KEY来标志。用了这个关键字,线程可以存取线程私有数据。

在线程特定数据中通常使用 4 个函数 

#include <pthread.h>

int pthread_key_create(pthread_key_t *key, void (*destructor)(void *value)  );

描述 : phtread_key_create 函数在进程内部分配一个标志TSD的关键字,参数key指向创建的关键字,该关键字对于一个进程中的所有线程是唯一的。所以在创建key时,每个进程只能调用一次创建函数pthrad_key_create()。 在key创建之前,所有线程的关键之值NULL,一旦关键字被建立,每个线程可以为该关键字绑定一个值。这个绑定的值对于线程是唯一的,每个线程独立维护。

参数destructor是一个可选的析构函数,可以和每个关键字联系起来。如果一个关键字的destruct()函数不为空,且线程为该关键字绑定了一个非空值,内马尔在线程退出时,析构函数将会被调用。对于所有关键字的析构函数,执行顺序是不能指定的。

该函数正常执行后返回值为0,否则返回错误代码。

#include <pthread.h>

int pthread_setspecific(pthread_key_t key, const  void *value);

pthread_setspecific()函数为TSD关键字绑定一个与本线程相关的值。参数key是TSD关键字;参数value是与本线程相关的值,value通常指向动态分配的内存区域

该函数正常执行后返回值为0,否则返回错误码。

#include <pthread.h>

void *pthread_getspecific(pthread_key_t key);

pthread_getspecific()函数获取与调用线程相关的TSD关键字所绑定的值。参数key是TSD关键字。

该函数正常执行后返回与调用线程相关的TSD关键字所绑定的值,否则返回NULL。

#include <pthread.h>

int pthread_once(pthread_once_t *once, void (*init)(void) );

函数使用once参数所指的变量,保证每个进程只调用一次init函数。通常once取常量 PTHREAD_ONCE_INT, 它保证每个进程只调用一次init函数

#include <stdio.h>
#include <pthread.h>
pthread_key_t   key;

void echomsg(void *value)
{
        printf("destructor excuted in thread %d,param=%d\n",pthread_self(),(int *)value);
}
void * child1(void *arg)
{
        pthread_t tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(2);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}
void * child2(void *arg)
{
        pthread_t tid = pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(1);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}
int main(void)
{
        pthread_t tid1,tid2;
        printf("hello\n");
        pthread_key_create(&key,echomsg);
        pthread_create(&tid1,NULL,child1,NULL);
        pthread_create(&tid2,NULL,child2,NULL);
        sleep(10);
        pthread_key_delete(key);
        printf("main thread exit\n");
        return 0;
}

下面是多线程共享同步共享静态变量的实例:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT 8028
#define BACKLOG 5
#define MAXDATASIZE 1000

void process(int connfd, struct sockaddr_in client);
void* function(void *arg);
void savadata(char *databuf,int num,char *returndata) ;

pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;

void destruct(void *ptr)
{
	free(ptr);
}

void create_init(void)
{
	pthread_key_create(&key, destruct);
}
struct ARG{
	int connfd;
	struct sockaddr_in client;
};

struct DATA
{
	int index;
};

int main()
{
	int listenfd,connfd;
	pthread_t tid;
	struct ARG *arg;
	struct sockaddr_in server;
	struct sockaddr_in client;
	if( (listenfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 )
	{
		printf("socket fail \n ");
		_exit(1);
	}

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)SO_REUSEADDR,sizeof(SO_REUSEADDR) );
	bzero( (void *)&server,sizeof(server) );

	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htonl(INADDR_ANY);           //服务器可能有许多网卡,但是INADDR_ANY 设置默认网卡IP  

	if( bind( listenfd,(struct sockaddr *)&server ,sizeof (server) ) == -1 )
	{
		printf(" bind fail \n");
		perror("Bind error");
		_exit(1);
	}

	if( listen(listenfd, BACKLOG) < 0)
	{
		printf("listen fail  \n");
		_exit(1);
	}
	printf("-----------服务器开始启动----------\n");
	//-------------开始监听客户端-----------------------------------------------
	while(1)
	{
		//int len = sizeof(client);
		if( (connfd = accept(listenfd, (struct sockaddr *)&client, &(socklen_t){sizeof client} ) ) == -1 )   //如果接受成功,那么client结构体将会存储远程主机的各种信息
		{
			printf("accept fail \n");
			_exit(1);
		}
		//printf("-----------服务器开始启动----------\n");
		arg = (struct ARG*)malloc(sizeof(struct ARG) );      //注意这里的arg是传给function函数的形参,一定需要进行内存分配,不然不能实现并发执行
		arg->connfd = connfd;
		memcpy(&arg->client, &client,sizeof(client) ) ;
		if( pthread_create(&tid,NULL,function,(void *)arg) )
		{	
			printf("can't create pthread \n");
			_exit(1);
		}
	}
	close(listenfd);
}

void savadata(char *databuf, int num, char *returndata)
{
	struct DATA *data;
	pthread_once(&once, create_init);

	if( (data = (struct DATA *)pthread_getspecific(key) ) == NULL)
	{
		data = (struct DATA *)malloc( sizeof(struct DATA) );
		pthread_setspecific(key, data);
		data->index = 0;
	}
	int i;
	for( i=0; i<num; i++)
	{
		returndata[data->index] = databuf[i];
		data->index++;
	}
	returndata[ data->index - 1];
	return ;
}
void process(int connfd, struct sockaddr_in client)
{
	int num;
	char cli_name[MAXDATASIZE];
	char databuf[MAXDATASIZE];
	char returndata[MAXDATASIZE];
	printf("get data from : %s", inet_ntoa( client.sin_addr) );
	
	if((num = recv(connfd, cli_name, MAXDATASIZE, 0) ) == 0 )
	{
		printf("client disconnected \n");
		return ;
	}
	cli_name[num-1] = '\0';
	printf("client name is %s \n", cli_name);

	while( num = recv(connfd, databuf, MAXDATASIZE, 0) )
	{
		databuf[num-1] = '\0';
		printf("get message from client : %s \n",databuf);
		send(connfd, databuf, strlen(databuf), 0);
		//对全局index变量进行控制
		savadata(databuf,num,returndata);		
		send(connfd, returndata, strlen(returndata), 0);
	}
	return ;
	
}

void *function(void *arg)
{
	struct ARG *info = (struct ARG*)arg;
	process(info->connfd,info->client);
	free(arg);
	pthread_exit(NULL);
}
很多线程公用其父进程的地址空间,其中相同变量可能被乱用,用线程函数加以控制,可以很好地解决此类问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值