unix高级环境编程 例子 代码实现练习 第十二章:线程控制

本文详细介绍了如何在并发环境中创建线程、使用递归互斥量、实现可重入版本的getenv函数以及同步信号处理等关键技术。通过具体代码示例,展示了线程安全编程的实践方法。

程序清单 12-1 以分离的状态创建的线程

/**
 * 程序清单 12-1 以分离的状态创建的线程 P315
 * zy:
 * 没什么好说的,说的很清楚了
 *
 */
#include <pthread.h>
#include "apue.h"

int makethread(void *(*fn)(void *),void *arg){
	int err;
	pthread_t tid;
	pthread_attr_t attr;

	err=pthread_attr_init(&attr);
	if(err!=0){
		return(err);
	}
	err= pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	if(err==0){		//成功了返回0
		err=pthread_create(&tid,&attr,fn,arg);
	}
	pthread_attr_destroy(&attr);
	return (err);
}

程序清单 12-2 使用递归互斥量 

/**
 * 程序清单 12-2 使用递归互斥量 P321
 * zy:
 * 确实使用了递归锁,但是我看不出为什么必须使用递归锁
 * 经过大量思考,大概因为323页的一段话
 * 因为
 * timetou需要互斥量来访问条件
 * retry需要原子操作
 *
 * 此外,完整定时操作的nanosleep函数是关键,它可以让线程暂停
 *
 */
#include <pthread.h>
#include "apue.h"
#include <time.h>
#include <sys/time.h>

int makethread(void *(*fn)(void *),void *arg){
	int err;
	pthread_t tid;
	pthread_attr_t attr;

	err=pthread_attr_init(&attr);
	if(err!=0){
		return(err);
	}
	err= pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	if(err==0){		//成功了返回0
		err=pthread_create(&tid,&attr,fn,arg);
	}
	pthread_attr_destroy(&attr);
	return (err);
}
struct to_info{
	void (* to_fn)(void *);
	void *to_arg;
	struct timespec to_wait;
};
#define SECTONSEC 1000000000	/*秒转化为纳秒*/
#define USECTONSEC 1000 		/*微妙到纳秒*/

void *timeout_helper(void *arg){
	struct to_info *tip;
	tip=(struct to_info *)arg;
	nanosleep(&tip->to_wait,NULL);	//这个函数功能是暂停某个线程直到你规定的时间后恢复,第一个就是你要暂停的时间
	(*tip->to_fn)(tip->to_arg);//这是一句函数在调用
	return 0;
}

void timeout(const struct timespec *when,void (*func)(void *),void *arg){
	struct timespec now;
	struct timeval tv;
	struct to_info *tip;
	int err;

	gettimeofday(&tv,NULL);condition
	now.tv_sec=tv.tv_sec;
	now.tv_nsec=tv.tv_usec*USECTONSEC;
	if(when->tv_sec>now.tv_sec||
			(when->tv_sec==now.tv_sec&&when->tv_nsec>now.tv_nsec)){
		tip=malloc(sizeof(struct to_info));
		if(tip!=NULL){
			tip->to_fn=func;
			tip->to_arg=arg;
			tip->to_wait.tv_sec=when->tv_sec-now.tv_sec;
			if( when->tv_nsec >= now.tv_nsec ){
				tip->to_wait.tv_nsec = when->tv_nsec-now.tv_nsec;
			}else{
				tip->to_wait.tv_sec--;
				tip->to_wait.tv_nsec=SECTONSEC-now.tv_nsec+when->tv_nsec;
			}
			err=makethread(timeout_helper,(void *)tip);
			if(err==0)
				return;
		}
	}
	/*
	 * 如果时间到了,now>=when,或者malloc失败了,或者我们不能创建函数,我们只需要现在调用函数
	 */
	(*func)(arg);
}

pthread_mutexattr_t attr;
pthread_mutex_t mutex;

void retry(void *arg){
	pthread_mutex_lock(&mutex);
	/*执行步骤*/
	pthread_mutex_unlock(&mutex);
}

int main(void){
	int err,condition,arg;
	struct timespec when;

	if((err=pthread_mutexattr_init(&attr))!=0){
		err_exit(err,"pthread_mutexattr_init failed");
	}
	if((err=pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE))!=0){
		err_exit(err,"can't set recuresive type");
	}
	if((err=phread_mutex_init(&mutex,&attr))!=0){
		err_exit(err,"can't create recursive mutex");
	}
	/*...*/
	pthread_mutex_lock(&mutex);
	/*...*/
	if(condition){
		/*calculate target time "when" */
		timeout(&when,retry,(void *)arg);
	}
	/*...*/
	pthread_mutex_unlock(&mutex);
	/*...*/
	exit(0);
}

程序清单 12-3 getenv 的非可重入版本

/**
 * 程序清单 12-3 getenv 的非可重入版本 P326
 * zy:
 * getenv就是获得环境变量
 * 可重入的就是线程安全的
 *
 * 运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
HOME: /home/asd
USER: asd
asd@asd-desktop:~/workspace/test/src$
 */

#include <string.h>
#include <limits.h>
#include <stdio.h>

//static char envbuf[ARG_MAX]; //明明定义在limits里面,不知道为什么取不了,编译也通不过
static char envbuf[131072]; /* # bytes of args + environ for exec() */
extern char **environ;  //环境变量都在这个里面
char *getenv(const char *name){
	int i,len;

	len = strlen(name);
	for (i=0;environ[i]!=NULL;i++){
		if((strncmp(name,environ[i],len)==0)
			&& environ[i][len]=='='){
				strcpy(envbuf,&environ[i][len+1]);
				return(envbuf);
			}
	}
	return NULL;
}
void  main(){
	getenv("HOME");
	printf("HOME: %s\n",envbuf);
	getenv("USER");
	printf("USER: %s\n",envbuf);
}

程序清单 12-4 getenv的可重入(线程安全)版本

/**
 * 程序清单 12-4 getenv的可重入(线程安全)版本
 */

#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>


extern char **environ;  //环境变量都在这个里面
pthread_mutex_t env_mutex;

static pthread_once_t init_done=PTHREAD_ONCE_INIT;

static void thread_init(void){
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);//书上解释了,而且中文有错误,我也在博客里指出来了
	pthread_mutex_init(&env_mutex,&attr);
	pthread_mutexattr_destroy(&attr);
}

int getenv_r(const char *name,char *buf,int buflen){
	int i,len,olen;
	pthread_once(&init_done,thread_init); //注意一个线程只会调用一次这个函数thread_init函数
	len=strlen(name);
	pthread_mutex_lock(&env_mutex);
	for (i=0;environ[i]!=NULL;i++){
		if((strncmp(name,environ[i],len)==0)
			&& environ[i][len]=='='){
			olen=strlen(&environ[i][len+1]);
			if(olen>=buflen){
				pthread_mutex_unlock(&env_mutex);
				return(ENOSPC);
			}
			strcpy(buf,&environ[i][len+1]);
			pthread_mutex_unlock(&env_mutex);
			return 0;
		}

	}
	pthread_mutex_unlock(&env_mutex);
	return (ENOENT);
}

程序清单 12-5 线程安全的getenv的兼容版本

/**
 * 程序清单 12-5 线程安全的getenv的兼容版本 P330
 * zy:
 * 看书还是要看慢点,不然很有些内容很困惑,回头一看又是没看懂
 *
 * 虽然使用了一个键
 * 但是不同线程访问的私有数据是不同的
 *
 */
#include </usr/include/linux/limits.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>

static pthread_key_t key;
static pthread_once_t init_done=PTHREAD_ONCE_INIT;
pthread_mutex_t env_mutex = PTHREAD_ONCE_INIT;
extern char **environ;  //环境变量都在这个里面

static void thread_init(void){
	pthread_key_create(&key,free);
}

int getenv_r(const char *name){
	int i,len;
	char *envbuf;

	pthread_once(&init_done,thread_init); //注意一个线程只会调用一次这个函数thread_init函数
	pthread_mutex_lock(&env_mutex);
	envbuf = (char *)pthread_getspecific(key);
	if(envbuf==NULL){
		envbuf=malloc(ARG_MAX); //必须要明确指明:#include </usr/include/linux/limits.h>
		if(envbuf==NULL){
			pthread_mutex_unlock(&env_mutex);
			return (NULL);
		}
	}
	len=strlen(name);
	for (i=0;environ[i]!=NULL;i++){
		if((strncmp(name,environ[i],len)==0)
			&& environ[i][len]=='='){
			strcpy(envbuf,&environ[i][len+1]);
			pthread_mutex_unlock(&env_mutex);
			return 0;
		}

	}
	pthread_mutex_unlock(&env_mutex);
	return (NULL);
}

程序清单 12-6 同步信号处理

/**
 * 程序清单 12-6 同步信号处理 P335
 * zy:
 * 用了一个专门的线程来处理信号,而不是信号处理程序
 * 
 * 
 * 运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
^C
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
					这里输入了^\
asd@asd-desktop:~/workspace/test/src$

 */
#include <pthread.h>
#include "apue.h"
#include "error.c"

int quitflag;
sigset_t mask;

pthread_cond_t wait=PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *thr_fn(void *arg){
	int err,signo;
	for(;;){
		err=sigwait(&mask,&signo);
		if(err!=0){
			err_exit(err,"sigwait failed");
		}
		switch (signo) {
			case SIGINT:
				printf("\ninterrupt\n");
				break;
			case SIGQUIT:
				pthread_mutex_lock(&lock);
				quitflag=1;
				pthread_mutex_unlock(&lock);
				pthread_cond_signal(&wait);
				break;
			default:
				printf("unexpected signal %d \n",signo);
				exit(1);
				break;
		}
	}
}
int main(void){
	int err;
	sigset_t oldmask;
	pthread_t tid;

	sigemptyset(&mask);
	sigaddset(&mask,SIGINT);
	sigaddset(&mask,SIGQUIT);

	if((err=pthread_sigmask(SIG_BLOCK,&mask,&oldmask))!=0){
		err_exit(err,"SIG_BLOCK failed");
	}
	err=pthread_create(&tid,NULL,thr_fn,0);
	if(err!=0){
		err_exit(err,"can't create thread");
	}
	pthread_mutex_lock(&lock);
	while(quitflag==0){
		pthread_cond_wait(&wait,&lock);
	}
	pthread_mutex_unlock(&lock);
	/**
	 * SIGQUIT已经在这里得到了,请写下其他代码
	 */
	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0){
		err_sys("SIG_SETMASK error");
	}
	exit(0);
}

程序清单 12-7 pthread_atfork 实例

/**
 * 程序清单 12-7 pthread_atfork 实例 P339
 * zy:
 * 主要是用fork之后继承了锁,用pthread_atfork来解决就好了
 * 运行结果:
asd@asd-desktop:~/workspace/test/src$ ./a.out
thread started ....
parent about to fork
preparing locks...
parent unlock...
parent returned from fork
child unlock...
child returned from fork
asd@asd-desktop:~/workspace/test/src$

 *
 */
#include <pthread.h>
#include "apue.h"
#include "error.c"

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void prepare(void){
	printf("preparing locks...\n");
	pthread_mutex_lock(&lock1);
	pthread_mutex_lock(&lock2);
}
void parent(void){
	printf("parent unlock...\n");
	pthread_mutex_unlock(&lock1);
	pthread_mutex_unlock(&lock2);
}
void child(void){
	printf("child unlock...\n");
	pthread_mutex_unlock(&lock1);
	pthread_mutex_unlock(&lock2);
}
void *thr_fun(void *arg){
	printf("thread started ....\n");
	pause();
	return 0;
}

int main(){
	int err;
	pid_t pid;
	pthread_t tid;

	if((err=pthread_atfork(prepare,parent,child))!=0){
		err_exit(err,"can't install fork handlers");
	}
	err=pthread_create(&tid,NULL,thr_fun,0);
	if(err!=0){
		err_exit(err,"can't create thread");
	}
	sleep(2);
	printf("parent about to fork\n");
	if((pid=fork())<0){
		err_quit("fork failed");
	}
	else if(pid ==0){
		printf("child returned from fork\n");
	}else{
		printf("parent returned from fork\n");
	}
	exit(0);
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值