Pthread 学习(1)
来源:互联网 发布:linux message日志分析 编辑:程序博客网 时间:2024/05/18 03:32
使用Pthread进行共享内存编程
1.简介
使用POSIX线程库来实线共享内存的访问,线程大体上是轻量级的进程,进程是正在运行(或挂起)的程序的一个实例,除了可执行代码外,它还包括 栈段,堆段,系统为进程分配的资源描述符(如文件描述符),安全信息(如进程允许访问的硬件和软件资源),描述进程状态的信息(程序计数器的数值等)
典型的共享内存“进程”允许了进程间互相访问各自内存区域,事实上,除了他们各自拥有独立的栈和程序计数器外,为了方便,它们基本上可以共享所有其他区域,为了方便管理,一般的方法是启动一个进程,然后由这个进程生成这些“轻量级”进程。
“轻量级”进程更通用的术语是线程。POSIX线程库是一个类UNIX操作系统上的标准库,定义了一套多线程编程应用程序的编程接口。Pthreads的API 只有在支持POSIX的系统(Linux, Max OS X, Solaris等)上才有效。
2一个简单的Pthread程序
#include <stdio.h>#include <stdlib.h>#include <pthread.h>int thread_count;void* Hello(void* rank);int main(int argc,char* argv[]){long thread;thread_count=strtol(argv[1],NULL,10);pthread_t thread_handles[thread_count];for(thread=0;thread<thread_count;thread++)pthread_create(&thread_handles[thread],NULL,Hello,(void*)thread);printf("Hello from the main thread\n");for(thread=0;thread<thread_count;thread++)pthread_join(thread_handles[thread],NULL);return 0;} void* Hello(void* rank){long my_rank=(long) rank;printf("Hello from thread %ld of %d\n",my_rank,thread_count);return NULL;}
编译时链接到pthread 库即可, gcc -o hello hello.c -lpthread
运行:
./hello 5
输出类似于:输出是并不确定的,因为每个线程先后都不一定
Hello from thread 3 of 5
Hello from the main thread
Hello from thread 1 of 5
Hello from thread 2 of 5
Hello from thread 0 of 5
Hello from thread 4 of 5
程序分析:
thread_count是一个全局变量,在Pthread程序中,全局变量被所有线程所共享,全局变量可能会在程序中引发令人困惑的错误,应该限制使用全局变量的使用,除了确实需要用到的情况外,比如线程之间共享变量。首先要定义个pthread_t 对象,来左右线程的调用对象,然后在创建线程之前要为这个pthread_t对象分配内存空间,pthread_t 数据结构用来存储线程专有信息,由pthread.h声明。调用pthread_create函数来生成线程,语法为,其中in和out只是为了说明此变量为输入变量还是输出变量int pthread_create(pthread_t* thread_p, //outconst pthread_attr_t* attr_p, //invoid* (*start_routine)(void* arg_p) //invoid* arg_p //in)第一个参数是一个指针,指向对应的pthread_t 对象,必须在调用pthrad_create前就为pthread_t对象分配内存空间;第二个参数可以用NULL(缺省值)第三个参数表示该线程将要运行的函数,此函数的形式通常为 void* thread_function(void* args_p)最后一个参数是一个指针,只想传给start_routine的参数给线程标注编号是由好处的,因为pthread_t对象是不透明的,所以不能用来输出,通过赋予线程编号,可以帮助我们了解在程序出错时是哪个线程发生了错误运行main函数的线程一般称为主线程,没有参数用于指定线程在哪个核上运行,线程的调度是由操作系统来控制的。调用pthread_join将等待pthread_t对象所关联的那个线程结束。int pthread_join(pthread_t thread , //invoid** rat_val_p //out)调用一次pthread_join 将等待pthread_t 对象所关联的那个线程结束第二个参数可以接收任意由pthread_t 对象所关联的那个线程结束
关于线程启动的一些认识:
在上面的例程中是通过键入参数来决定生成多少个线程,然后由主线程来生成这些“辅助”线程。
还有一种做法是,请求到来后,主线程启动辅助线程来进行请求处理,例如WEB服务器
需要知道的是,线程的启动开销是比较大的,所以“按需启动线程”也许并不是使应用程序性能最优化的理想方法
二.临界区
当多个线程需要更新同一内存单元时,如果至少其中一个访问是更新操作,那么这些访问就可能会导致某种错误,我们称为竞争条件。
三,忙等待
使用一个共享的标识量flag,在下面的程序中,当线程数为2时,运行时间为18秒左右,当线程数目为1时,运行时间仅需要2秒,这主要是忙等待中:while(flag!=my_rank)语句,flag初始化的值为0,所以在线程0完成临界区运算并将flag加1之前,线程1必须等待,同理,当线程1进入临界区后,线程0必须等待线程1完成运算。所以,线程不停的在等待和运行之间切换,所以是非常耗时的!
int flag=0;int n=100000000;int thread_count=2;void* Thread_sum(void* rank);double sum;int main(){long thread;pthread_t thread_handles[thread_count];clock_t start_time=clock(); for(thread=0;thread<thread_count;thread++)pthread_create(&thread_handles[thread],NULL,Thread_sum,(void*)thread);for(thread=0;thread<thread_count;thread++)pthread_join(thread_handles[thread],NULL);printf("%lf\n",sum*4);clock_t end_time=clock(); double runtime=(double)(end_time-start_time)/CLOCKS_PER_SEC;printf("Running time is %f S:\n",runtime);return 0;}void* Thread_sum(void* rank){long my_rank=(long)rank;double factor;long long i;long long my_n=n/thread_count;long long my_fisrt_i=my_n*my_rank;long long my_last_i=my_fisrt_i+my_n;if(my_fisrt_i%2==0)factor=1.0;elsefactor=-1.0;for(i=my_fisrt_i;i<my_last_i;i++,factor=-factor){while(flag!=my_rank);sum+=factor/(2*i+1);flag=(flag+1)%thread_count;}return NULL;}
忙等待不是保护临界区的唯一方法,事实上,还有很多更好的方法,然而,因为临界区中的代码一次只能由一个线程运行,所以无论如何限制访问临界区,都必须串行地执行其中的代码。如果可能的话,我们应该 执行临界区的次数。能够大幅度提高性能的一个方法是:给每个线程配置私有变量来存储各个部分的和,然后for循环一次性将所有部分和加在一起算出总和。
void* Thread_sum(void* rank){long my_rank=(long)rank;double factor,my_sum=0.0;long long i;long long my_n=n/thread_count;long long my_fisrt_i=my_n*my_rank;long long my_last_i=my_fisrt_i+my_n;if(my_fisrt_i%2==0)factor=1.0;elsefactor=-1.0;for(i=my_fisrt_i;i<my_last_i;i++,factor=-factor){my_sum+=factor/(2*i+1);}while(flag!=my_rank);printf("this is thread %ld\n",my_rank);sum+=my_sum;flag=(flag+1)%thread_count;return NULL;}
四.互斥量
互斥量是互斥锁的简称,它是一个特殊类型变量的简称,通过某些特殊类型的函数,互斥量可以用来限制每次只有一个线程能够进入临界区。
Pthreads标准为互斥量提供了一个特殊类型:pthread_mutex_t,在使用pthread_mutex_t类型之前,必须由系统对其进行初始化,初始化函数为:
int pthread_mutex_init( pthread_mutex_t* mutex_p, const pthread_mutexattr_t* attr_p )我们不使用第二个参数,给这个参数赋值NULL即可,当一个pthread程序使用完互斥量,它应该调用
int pthread_mutex_destroy( pthread_mutex_t* mutex_p)
要获得临界区的访问权,线程需要调用
int pthread_mutex_lock( pthread_mutex_t * mutex_p)
当线程退出临界区后,线程应该调用:
int pthread_mutex_unlock( pthread_mutex_t* mutex_p)
通过声明一个全局的互斥量,可以在全局求和的程序中用互斥量来代替忙等待,主线程对互斥量进行初始化,当线程进入临界区前调用pthread_mutex_lock,在执行完临界区中的所有操作后调用pthread_mutex_unlock.。第一个调用pthrad_mutex_lock的线程
会为临界区上锁,其他线程如果想要进入临界区,也需要调用pthread_mutex_lock,这些调用了pthread_mutex_lock的线程都会阻塞并等待,直到第一个线程调用了pthread_mutex_unlock离开临界区
long n=500000000;int thread_count=10;void* Thread_sum(void* rank);double sum;pthread_mutex_t mutex;int main(){long thread; pthread_mutex_init(&mutex,NULL);pthread_t thread_handles[thread_count];clock_t start_time=clock(); for(thread=0;thread<thread_count;thread++)pthread_create(&thread_handles[thread],NULL,Thread_sum,(void*)thread);for(thread=0;thread<thread_count;thread++)pthread_join(thread_handles[thread],NULL);printf("%lf\n",sum*4);clock_t end_time=clock(); double runtime=(double)(end_time-start_time)/CLOCKS_PER_SEC;printf("Running time is %f S:\n",runtime);return 0;}void* Thread_sum(void* rank){long my_rank=(long)rank;double factor,my_sum=0.0;long long i;long long my_n=n/thread_count;long long my_fisrt_i=my_n*my_rank;long long my_last_i=my_fisrt_i+my_n;if(my_fisrt_i%2==0)factor=1.0;elsefactor=-1.0;for(i=my_fisrt_i;i<my_last_i;i++,factor=-factor)my_sum+=factor/(2*i+1);pthread_mutex_lock(&mutex);sum+=my_sum;pthread_mutex_unlock(&mutex);return NULL;}
在使用互斥量的多线程程序中,多个线程进入临界区的顺序是随机的,线程顺序由系统负责分配
- Pthread 学习(1)
- pthread学习笔记(一)
- pthread 学习
- pthread学习
- pthread学习
- pthread学习
- pthread库学习(1): 线程的创建
- pthread系列函数实例学习(一)
- Pthread(1)
- pthread学习笔记(二)--pthread常用API及简单介绍
- pthread学习笔记(三)--跋涉之旅之Posix线程编程指南(1)
- pthread库学习
- pthread 学习总结
- Pthread学习完毕
- pthread学习笔记
- pthread学习笔记
- pthread 学习笔记
- linux pthread线程学习
- jQuery metadata.js
- a hotfix to resolve an issue with memory mapped files on Windows
- linux压缩和解压缩命令大全
- Deprecated: mysql_escape_string(): This function is deprecated; use mysql_real_escape_string() inste
- RBAC介绍
- Pthread 学习(1)
- java代码块与构造函数
- Android面试过程描述
- Android项目中使用自定义进度加载Dialog
- 查看两个int数的二进制有几位不同
- 关于android.intent.action.MAIN和android.intent.category.LAUNCHER的简单理解
- JAVA修饰符类型(public,protected,private,friendly)
- Qt Model/View模型
- Opencv图像处理坐标系认识