`

(第四章 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);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    嵌入式软件开发技术:第5章 嵌入式Linux多线程编程.ppt

    嵌入式Linux多线程编程 嵌入式Linux多线程编程是嵌入式系统开发中的一种重要技术,能够提高系统的效率和响应速度。本章节将详细介绍嵌入式Linux多线程编程的基本概念、线程的创建、同步和互斥、线程属性、多线程...

    Linux多线程编程的高效开发经验.

    总之,Linux多线程编程涉及到线程创建、同步机制、线程安全和性能优化等多个方面。理解并熟练运用Pthread库提供的API,遵循上述实践,可以有效地避免开发陷阱,提升多线程程序的稳定性和效率。在实际开发过程中,...

    实验二:Linux多线程创建.docx

    Linux 多线程创建实验 本实验的目的是为了进一步掌握在 Linux 系统进行 C 语言编程的方法,进一步了解线程的概念,进一步理解进程与线程的概念,并掌握 C 语言线程创建的方法。 实验原理: 1. Pthread_create ...

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

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

    linux多线程编程.doc

    在Linux系统中进行C语言的多线程编程,可以利用POSIX线程库(pthread库)提供的接口。...理解这些基本概念和函数是进行Linux多线程编程的基础,而通过实践编写和调试多线程程序,可以更好地理解和掌握这些知识。

    linux多线程编程

    ### Linux多线程编程知识点详解 #### 一、POSIX标准与Linux多线程编程的关系 POSIX(Portable Operating System Interface)是IEEE(电气和电子工程师协会)制定的一系列标准,旨在提高UNIX环境下应用程序的可移植...

    linux-uboot和套结字多线程

    Linux U-Boot与套接字多线程是嵌入式系统开发中的两个关键概念,它们在构建和操作基于Linux的嵌入式设备时起着至关重要的作用。U-Boot是通用引导加载程序(Universal Boot Loader)的缩写,是嵌入式系统启动时的第一...

    UNIX Linux实验教程 5实验五Linux多线程程序设计.doc

    本资源是关于UNIX Linux实验教程的第五实验,主要讲解Linux多线程程序设计的知识点。该实验的目的是让学生加深对线程概念的理解,并掌握使用 POSIX 线程机制进行多线程应用程序的编程方法。 实验指导中,首先讲解了...

    嵌入式Linux多线程编程.zip

    在压缩包中的"第13章_Linux多线程编程"文件中,可能会包含具体的C语言源码示例,展示如何创建、管理线程,以及如何使用同步机制解决并发问题。通过学习这些示例,开发者可以更深入地理解如何在嵌入式Linux系统上进行...

    第9章、多线程编程_linux_

    在Linux系统中,多线程编程是实现并发执行任务的重要方式。...阅读《嵌入式Linux应用程序开发标准教程》的第9章,你将深入理解Linux环境下的多线程编程,掌握如何利用这一强大的工具来提升你的程序性能。

    基于Linux C语言的多线程模拟智能家具服务器源代码

    在本项目中,我们关注的是一个基于Linux操作系统,使用C语言编写的多线程智能家具服务器的源代码。这个服务器程序旨在模拟智能家居环境中的设备控制,可能是为了学习、实验或开发目的。以下是对这个项目中涉及的技术...

    linux下c语言多线程网页爬虫源代码

    综上所述,"linux下c语言多线程网页爬虫源代码"这个项目涵盖了Linux环境下的C语言编程、网络编程、多线程技术以及网页爬虫的设计和实现。在实际操作中,开发者需要深入理解这些知识点,并结合具体需求来优化爬虫的...

    Linux网络编程(四)——多线程实现简单的聊天(linux 服务器端 windows客户端)

    在本篇Linux网络编程系列的第四部分中,我们将探讨如何使用多线程技术来实现一个简单的跨平台聊天应用。这个应用包含一个运行在Linux服务器端的程序和一个运行在Windows客户端的程序。通过多线程,我们可以提高系统...

    Linux多线程编程.docx

    Linux多线程编程是操作系统编程中的一个重要概念,它允许在一个进程中创建多个执行线程,使得程序可以并行处理任务,提高资源利用率和程序性能。在给出的代码中,可以看到一个简单的Linux多线程示例,涉及到的主要...

    嵌入式Linux应用程序开发详解-第9章(多线程编程)

    ### 嵌入式Linux应用程序开发详解 - 第9章 多线程编程 #### 9.1 Linux下线程概述 ##### 9.1.1 线程概述 线程作为轻量级进程,是现代操作系统中重要的编程概念之一。在Linux中,线程的引入主要是为了提高系统的...

    linux多线程

    ### Linux多线程编程知识点详解 #### 多线程基础介绍 **定义多线程术语:** - **线程**:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些...

Global site tag (gtag.js) - Google Analytics