(第四章 1)Linux多线程

相关函数定义在/usr/include/下,如/usr/include/pthread.h中。

参考文档:

Linux多线程编程  http://www.cnblogs.com/feisky/archive/2009/11/12/1601824.html

Linux下的多线程编程 http://fanqiang.chinaunix.net/a4/b8/20010811/0905001105.html

理解“内核线程”,"轻量级进程LWP","用户线程" http://www.cnitblog.com/tarius.wu/articles/2277.html

POSIX(wiki):

     an acronym for "Portable Operating System Interface", is a family of standards specified by the IEEE for maintaining compatibility between operating systems. POSIX defines the application programming interface (API), along with command line shells and utility interfaces, for software compatible with variants of Unix and other operating systems.

 

 

 

简单多线程编程

一、线程的绑定状态和游离状态

1. 创建的线程处于undetached状态

默认情况下,pthread_create()创建的线程为“被绑定状态(undetached)”,即,该线程被绑定到一个LWP上。

此时,线程线程终止时刻,并不马上释放自己占用的系统资源,而是由创建者调用pthread_join()等待其返回后释放它占用的系统资源。

 

2. 创建的线程处于detached状态

如果设置一个线程为“游离(detached)”线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。

要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题

 

二、编译指令

> gcc -g simple.c -lpthread -o simple

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

void thread(void){
	int i;
	for(i=0;i<10;i++){
		printf("This is a pthread.\n");
	}
}

int main(void){
	pthread_t id;
	int i;
	//int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (*start_routine)(void *), void *arg);
	if(!pthread_create(&id,NULL,(void*)thread,NULL)){
		printf("pthread_create() success!\n");

		//本函数等待被创建的线程返回才返回,并释放被创建线程占用的系统资源
		//void pthread_join(pthread_t th, void* thread_return); 其中*thread_return=retval
		pthread_join(id,NULL);

		for(i=0;i<3;i++){
			printf("this is the main process.\n");
		}
		//(1)主线程如果从main函数return或是调用exit函数退出,则整个进程终止(所有的其他线程也将终止);
		//(2)主线程调用pthread_exit函数,则仅主线程消亡,进程不会结束,其他线程不受影响。
		//pthread_exit(0);
		return 0;
	}else{
		printf("pthread_create() failure!\n");
		return 1;
	}
}

 

 

 

线程局部存储

两个线程对自己的私有数据操作是互相不影响的。也就是说,虽然 key 同名且全局,但访问的内存空间并不相同。key就像是一个数据管理员,线程的私有数据只是到他那去注册,让它知道你这个数据的存在。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_key_t key;  //一个私有数据类型变量可以存放任意类型数据,使用时候强制转换即可

struct test_struct{
	int i;
	float k;
};

void print1(void){
	printf("0x%p\n",(struct test_struct*)pthread_getspecific(key));
	printf("%d, %f\n", ((struct test_struct*)pthread_getspecific(key))->i,
				((struct test_struct*)pthread_getspecific(key))->k);
}

void print2(void){
	printf("0x%p\n",(int*)pthread_getspecific(key));
	printf("%d\n", *((int*)pthread_getspecific(key)));
}

void *child1(void *arg){
	struct test_struct struct_data;
	struct_data.i=10;
	struct_data.k=3.1415;

	//每个线程就像访问全局变量那样访问变量key,实际上变量是线程私有的
	pthread_setspecific(key, &struct_data);

	printf("0x%p\n",&struct_data);
	print1();
}

void *child2(void *arg){
	int temp=20;
	sleep(2);
	
	pthread_setspecific(key,&temp);
	printf("0x%p\n",&temp);
	print2();
}

int main(void){
	pthread_t tid1,tid2;
	pthread_key_create(&key,NULL);			//

	pthread_create(&tid1,NULL,(void*)child1,NULL);
	pthread_create(&tid2,NULL,(void*)child2,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	pthread_key_delete(key);			//

	return 0;
}

 

 

同步机制

(1)互斥锁; (2)条件变量; (3)信号量

 

(1)互斥锁

/*
linux下为了多线程同步,通常用到锁的概念。
posix下抽象了一个锁类型的结构:ptread_mutex_t。通过对该结构的操作,来判断资源是否可以访问。顾名思义,加锁(lock)后,别人就无法打开,只有当锁没有关闭(unlock)的时候才能访问资源。
它主要用如下5个函数进行操作。
1:pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t *attr);
初始化锁变量mutex。attr为锁属性,NULL值为默认属性。
2:pthread_mutex_lock(pthread_mutex_t *mutex);加锁
3:pthread_mutex_tylock(pthread_mutex_t *mutex);加锁,但是与2不一样的是当锁已经在使用的时候,返回为EBUSY,而不是挂起等待。
4:pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁
5:pthread_mutex_destroy(pthread_mutex_t *mutex);使用完后释放

Sam: “互斥锁”就是我在OS或者数据库课程中学到的“锁”概念。

下面经典例子为创建两个线程对sum从1加到100。前面第一个线程从1-49,后面从50-100。主线程读取最后的加值。为了防止资源竞争,用了pthread_mutex_t 锁操作。
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t lock;
int sum;

void *add1(void *sum){
	pthread_mutex_lock(&lock);	//加锁

	int i;
	for(i=0;i<50;i++)
		(*(int*)sum)+=i;

	pthread_mutex_unlock(&lock);	//解锁
	pthread_exit(NULL);
}

void *add2(void *sum){
	pthread_mutex_lock(&lock);	//加锁

	int i;
	for(i=50;i<101;i++)
		(*(int*)sum)+=i;

	pthread_mutex_unlock(&lock);	//解锁
	pthread_exit(NULL);
}

int main(void){
	int i;
	pthread_t ptid1,ptid2;
	sum=0;

	pthread_mutex_init(&lock,NULL);	//*创建锁

	pthread_create(&ptid1,NULL,add1,&sum);
	pthread_create(&ptid2,NULL,add2,&sum);
	pthread_join(ptid1,NULL);
	pthread_join(ptid2,NULL);

	pthread_mutex_lock(&lock);	//加锁
	printf("sum %d\n",sum);
	pthread_mutex_unlock(&lock);	//解锁

	pthread_mutex_destroy(&lock);	//*销毁锁
	return 0;
}

 

(2)条件变量

/*
条件变量 pthread_cond, 是另外一种线程间的同步机制。普通的 mutex 只允许一个线程进入临界区,就是拿到mutex这把锁的线程,而cond 允许多个线程同时进入临界区,由它来控制,在某些条件成立的时候,来唤醒其中一个等待着的线程,或者是唤醒所有等待着的线程。

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
int pthread_cond_timewait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* tout)

传递给pthread_cond_wait的互斥量mutex对条件进行保护,调用者把锁住的互斥量传递给pthread_cond_wait函数,函数把调用线程放到等待条件的线程列表里面,然后对互斥量解锁,当pthread_cond_wait返回的时候,互斥量再次被锁住。函数pthread_cond_timewait与pthread_cond_wait差不多,只不过是多了超时时间的限制。
两个函数调用成功返回的时候,需要重新检查条件,因为其他线程可能更改了条件。

int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);

pthread_cond_signal 函数将唤醒等待该条件的某个线程,pthread_cond_broadcast 将唤醒等待改条件的所有线程。

下面的例子很简单的使用了 cond 。 使用cond我们可以比较高效的写出一个线程池。
*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int val=0;

/*
	一旦val>2,就将其置0
*/
void *thread_zero_run(void *arg){
	while(1){
		pthread_mutex_lock(&mutex);

		while(val <= 2){
			printf("thread_zero_run --> val:%d, wait for wake up\n", val);
			//当条件变量cond被激活时,尝试获得锁mutex。这里包含了一个pthread_mutex_lock(&mutex)的操作:)
			pthread_cond_wait(&cond, &mutex);
		}
		printf("therad_zero_run --> val:%d, zero it and unlock\n", val);
		val = 0;

		pthread_mutex_unlock(&mutex);
	}

	pthread_exit(NULL);
}

/*
	每休息一秒钟尝试对val++
*/
void *thread_add_run(void *arg){
	while(1){
		pthread_mutex_lock(&mutex);
		++val;
		pthread_mutex_unlock(&mutex);

		pthread_cond_signal(&cond);	//激活条件变量cond(激活所有等待线程)

		printf("after add val: %d and wake up one zero thread for check\n",val);
		sleep(1);
	}
	pthread_exit(NULL);
}

int main(void){
	pthread_t t_add,t_zero;

	pthread_cond_init(&cond,NULL);	//*初始化条件变量

	if(pthread_create(&t_add, NULL, thread_add_run, NULL)){
		return 1;
	}

	if(pthread_create(&t_zero, NULL, thread_zero_run, NULL)){
		return 1;
	}

	pthread_join(t_add,NULL);
	pthread_join(t_zero,NULL);

	pthread_cond_destroy(&cond);	//*销毁条件变量

	return 0;
}

 

(3)信号量

/*
如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。
#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。

两个原子操作函数:
int sem_wait(sem_t *sem):给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。
int sem_post(sem_t *sem):给信号量的值加1
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。

int sem_destroy(sem_t *sem);
用完信号量后都它进行清理。归还自己占有的一切资源

Sam: 信号量sem_t semaphore实际上就是资源的个数。
	(1)在使用前,sem_wait(),等待资源个数>0,然后争取抢到资源,随后#semaphore--
	(2)使用完后,sem_post(),归还资源(semaphore)

参见经典的“生产者-消费者”例子,明天继续:)
*/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>

sem_t sem;


void *thread_fun1(void *arg){
	while(1){

	int semvalue=0;
	sem_getvalue(&sem, &sem_value);
	printf("sem_fun1(begin): %d\n",sem_value);

	sem_wait(&sem);				//等待信号量
	sem_getvalue(&sem, &sem_value);
	sem_post(&sem);				//归还信号量
	
	printf("sem_fun1(end): %d\n",sem_value);

	sleep(1);

	}
}

void *thread_fun2(void *arg){
	while(1){

	int semvalue=0;
	sem_getvalue(&sem, &sem_value);
	printf("sem_fun2(begin): %d\n",sem_value);

	sem_wait(&sem);				//等待信号量
	sem_getvalue(&sem, &sem_value);
	sem_post(&sem);				//归还信号量

	sem_getvalue(&sem, &sem_value);
	printf("sem_fun2(end): %d\n",sem_value);

	sleep(1);

	}
}

int main(void){
	pthread_t tid;
	void *thread_result;

	if(sem_init(&sem,0,2)!=0){		//*初始化信号量
		perror("semaphore init failed");
	}


	if(pthread_create(&tid,NULL,thread_fun1,NULL)!=0){
		perror("thread1 creation failed");
	}

	sleep(5);
	
	if(pthread_create(&tid,NULL,thread_fun2,NULL)!=0){
		perror("thread2 creation failed");
	}

	sem_destroy(&sem);			//*销毁信号量

	printf("主线程退出\n");
	pthread_exit(0);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(linux)