`

(第四章 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多线程编程

    linux多线程编程 声明:本文是网上整理的资料,版权属其作者本人所有。 1 第一章 线程基础知识 2 一.什么是线程 2 二.线程的优点 2 三.线程的缺点 2 四.线程的结构 2 五.线程标识 2 六.线程的创建 3 七..线程...

    第四章 LINUX 进程与线程

    Linux是一个多任务的操作系统,也就是说,在同一个时间内,可以有多个进程同时执行。原来Linux使用了一种称为“进程调度(process scheduling)”的手段

    Linux多线程服务端编程:使用muduo C++网络库

    《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...

    嵌入式Linux应用程序开发详解(华清).zip

    第1 章 Linux 快速入门 第2 章 Linux 基础命令 第3 章 Linux 下的C 编程基础 第4 章 嵌入式系统基础 第5 章 嵌入式Linux开发环境的搭建 第6 章 文件IO编程 第7 章 进程控制开发 第8 章 进程间通信 第9章(多线程编程)...

    linux系统下多线程编程文档资料

    第四章 互斥量 39 一、什么是互斥锁 39 二、初始化/回收互斥锁 40 三、对互斥量加减锁 40 四、互斥锁属性 45 五、应用互斥量需要注意的几点 48 第五章 条件变量 48 一、什么是条件变量 48 二、条件变量函数 48 三、...

    嵌入式linux应用程序开发技术详解(华清远见 黑色经典)

    第1章(Linux快速入门) 第2章(Linux基础命令) 第3章(Linux下的C编程基础) 第4章(嵌入式系统基础) 第5章(嵌入式Linux开发环境搭建) 第6章(文件IO编程) 第7章(进程控制开发) 第8章(进程间通信) 第9章(多线程编程) 第10...

    多线程编程 UNIX LINUX Programming with POSIX Threads 第4部分

    在大中华区,我相信此书网上仅此一本.由本人制作 . 我在很多论坛上看到类似的主题:求书:...希望对大家有帮助. 文件太大,分成了四部分,请下载完整. &lt;br&gt;多线程编程 UNIX LINUX Programming with POSIX Threads

    linux 嵌入式开发

    第1章、Linux快速入门.pdf 第2章、Linux基础命令.pdf 第3章、Linux下C编程基础.pdf 第4章、嵌入式系统基础.pdf 第5章、嵌入式Linux开发环境的搭建.pdf 第6章、文件IO编程.pdf 第7章、进程控制开发.pdf 第8章、进程间...

    嵌入式Linux应用程序开发标准教程2版

    第1章、Linux快速入门 第2章、Linux基础命令 第3章、Linux下C编程基础 第4章、嵌入式系统基础 第5章、嵌入式Linux开发环境的搭建 第6章、文件IO编程 第7章、进程控制开发 第8章、进程间通信 第9章、多线程编程 第10...

    嵌入式linux应用程序开发详解

    第4 章 嵌入式系统基础.pdf 第5 章 嵌入式Linux开发环境的搭建.pdf 第6 章 文件IO编程.pdf 第7 章 进程控制开发.pdf 第8 章 进程间通信.pdf 第9 章(多线程编程).pdf 第10 章(嵌入式Linux网络编程).pdf 第11 章...

    Linux系统编程之线程同步

    所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成...

    多线程编程 UNIX LINUX Programming with POSIX Threads 第2部分

    在大中华区,我相信此书网上仅此一本.由本人制作 . 我在很多论坛上看到类似的主题:求书:...希望对大家有帮助. 文件太大,分成了四部分,请下载完整. &lt;br&gt;多线程编程 UNIX LINUX Programming with POSIX Threads

    多线程编程 UNIX LINUX Programming with POSIX Threads 第3部分

    在大中华区,我相信此书网上仅此一本.由本人制作 . 我在很多论坛上看到类似的主题:求书:...希望对大家有帮助. 文件太大,分成了四部分,请下载完整. &lt;br&gt;多线程编程 UNIX LINUX Programming with POSIX Threads

    嵌入式linux应用程序开发详解(zip)

    第4 章 嵌入式系统基础.pdf 第5 章 嵌入式Linux开发环境的搭建.pdf 第6 章 文件IO编程.pdf 第7 章 进程控制开发.pdf 第8 章 进程间通信.pdf 第9 章(多线程编程).pdf 第10 章(嵌入式Linux网络编程).pdf 第11 章...

    嵌入式Linux应用程序开发详解

    嵌入式Linux应用程序开发详解-第4 章 嵌入式系统基础.pdf 嵌入式Linux应用程序开发详解-第5 章 嵌入式Linux开发环境的搭建.pdf 嵌入式Linux应用程序开发详解-第6 章 文件IO编程.pdf 嵌入式Linux应用程序开发详解-第7...

    嵌入式Linux应用程序开发详解 中文版

    嵌入式Linux应用程序开发详解 嵌入式Linux应用程序开发详解-第1 章 Linux 快速入门.pdf 嵌入式Linux应用程序开发详解-第10章(嵌入式Linux网络编程).pdf ... 嵌入式Linux应用程序开发详解-第9章(多线程编程).pdf

    GNU/Linux编程指南(第二版)中文版含光盘

    第1章 Linux及Linux编程综述 第2章 设置开发系统 第3章 使用GNU CC 第4章 使用GNU make管理项目 第5章 创建可移植的自配置软件 第6章 比较和合并源代码文件 第7章 使用RCS和CVS控制版本 第8章 调试 第9章 出错处理 ...

    Linux进程和线程的基本编程、通讯和例程1

    设计模式 POSIX多线程程序设计(第4章:使用线程的几种方式)瓦釜苑-CSDN博客posix多线程程序设计。调试相关:Linux进程崩溃原调试_guotian

    (带详细书签)Linux系统编程用例及详解

    第一章:linux基础知识 第二章:linux下编译与调试 第三章:linux文件目录操作 第四章:linux多进程 第五章:linux信号处理 ...第七章:linux多线程 第八章:linux网络编程 第九章:系统编程的一个小程序

Global site tag (gtag.js) - Google Analytics