2多线程应用程序设计.DOC

上传人:天*** 文档编号:1320823 上传时间:2019-02-08 格式:DOC 页数:10 大小:167KB
下载 相关 举报
2多线程应用程序设计.DOC_第1页
第1页 / 共10页
2多线程应用程序设计.DOC_第2页
第2页 / 共10页
2多线程应用程序设计.DOC_第3页
第3页 / 共10页
2多线程应用程序设计.DOC_第4页
第4页 / 共10页
2多线程应用程序设计.DOC_第5页
第5页 / 共10页
点击查看更多>>
资源描述

1、2.2 多线程应用程序设计一、实验目的 了解多线程程序设计的基本原理。 学习 pthread 库函数的使用。 二、实验内容 读懂 pthread.c 的源代码,熟悉几个重要的 PTHREAD 库函数的使用。掌握共享锁和信号量的使用方法。 进入/arm2410s/exp/basic/02_pthread 目录,运行 make 产生 pthread 程序,使用 NFS 方式连接开发主机进行运行实验。 三、预备知识 有 C 语言基础 掌握在 Linux 下常用编辑器的使用 掌握 Makefile 的编写和使用 掌握 Linux 下的程序编译与交叉编译过程 四、实验设备及工具 硬件:UP-NETARM

2、2410-S 嵌入式实验平台,PC 机 Pentium 500 以上, 硬盘 40G 以上,内存大于 128M。 软件:PC 机操作系统 REDHAT LINUX 9.0 MINICOM ARM-LINUX 开发环境五、实验原理及代码分析 1多线程程序的优缺点 多线程程序作为一种多任务、并发的工作方式,有以下的优点: 1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming )置于一个新的线程,可以避免这种尴尬的情况。 2) 使多 CPU 系统更加

3、有效。操作系统会保证当线程数不大于 CPU 数目时,不同的线程运行于不同的 CPU 上。 3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。 LIBC 中的 pthread 库提供了大量的 API 函数,为用户编写应用程序提供支持。 2实验源代码与结构流程图 本实验为著名的生产者消费者问题模型的实现,主程序中分别启动生产者线程和消费者线程。生产者线程不断顺序地将 0 到 1000 的数字写入共享的循环缓冲区,同时消费者线程不断地从共享的循环缓冲区读取数据。流程图如图 2.2.1 所示:本实验具体代码如下: /* * The

4、 classic producer-consumer example. * Illustrates mutexes and conditions. * by Zou jian guo * 2003-12-22 */ #include #include #include #include “pthread.h“ #define BUFFER_SIZE 16 /* 设置一个整数的圆形缓冲区 */ struct prodcons int bufferBUFFER_SIZE; /* 缓冲区数组 */ pthread_mutex_t lock; /* 互斥锁 */ int readpos, writep

5、os; /* 读写的位置*/pthread_cond_t notempty; /* 缓冲区非空信号 */ pthread_cond_t notfull; /*缓冲区非满信号 */ ; /*-*/ /*初始化缓冲区*/ void init(struct prodcons * b) pthread_mutex_init( pthread_cond_init( pthread_cond_init( b-readpos = 0; b-writepos = 0; /*-*/ /* 向缓冲区中写入一个整数 */ void put(struct prodcons * b, int data) pthread

6、_mutex_lock( /*等待缓冲区非满*/ while (b-writepos + 1) % BUFFER_SIZE = b-readpos) printf(“wait for not fulln“); pthread_cond_wait( /*写数据并且指针前移*/ b-bufferb-writepos = data; b-writepos+; if (b-writepos = BUFFER_SIZE) b-writepos = 0; /*设置缓冲区非空信号*/ pthread_cond_signal( pthread_mutex_unlock( /*-*/ /*从缓冲区中读出一个整数

7、 */ int get(struct prodcons * b) int data; pthread_mutex_lock(/* 等待缓冲区非空*/ while (b-writepos = b-readpos) printf(“wait for not emptyn“); pthread_cond_wait( /* 读数据并且指针前移 */ data = b-bufferb-readpos; b-readpos+; if (b-readpos = BUFFER_SIZE) b-readpos = 0; /* 设置缓冲区非满信号 */ pthread_cond_signal( pthread_m

8、utex_unlock( return data; /*-*/ #define OVER (-1) struct prodcons buffer; /*-*/ void * producer(void * data) int n; for (n = 0; n %dn“, n); put( put( printf(“producer stopped!n“); return NULL; /*-*/ void * consumer(void * data) int d; while (1) d = get( if (d = OVER ) break; printf(“ %dgetn“, d); pr

9、intf(“consumer stopped!n“); return NULL; /*-*/ int main(void) pthread_t th_a, th_b; void * retval; init( pthread_create( pthread_create( /* 等待生产者和消费者结束 */ pthread_join(th_a, pthread_join(th_b, return 0; 3主要函数分析: 下面我们来看一下,生产者写入缓冲区和消费者从缓冲区读数的具体流程,生产者首先要获得互斥锁,并且判断写指针+1 后是否等于读指针,如果相等则进入等待状态,等候条件变量 notfu

10、ll;如果不等则向缓冲区中写一个整数,并且设置条件变量为 notempty,最后释放互斥锁。消费者线程与生产者线程类似,这里就不再过多介绍了。流程图如下:生产者写入共享的循环缓冲区函数 PUT void put(struct prodcons * b, int data) pthread_mutex_lock( /获取互斥锁 while (b-writepos + 1) % BUFFER_SIZE = b-readpos) /如果读写位置相同 pthread_cond_wait( /等待状态变量 b-notfull,不满则跳出阻塞 b-bufferb-writepos = data; /写入数

11、据 b-writepos+; if (b-writepos = BUFFER_SIZE) b-writepos = 0; pthread_cond_signal( /设置状态变量 pthread_mutex_unlock( /释放互斥锁 消费者读取共享的循环缓冲区函数 GET int get(struct prodcons * b) int data; pthread_mutex_lock( /获取互斥锁 while (b-writepos = b-readpos) /如果读写位置相同 pthread_cond_wait( /等待状态变量 b-notempty,不空则跳出阻塞。否则无数据可读。

12、 data = b-bufferb-readpos; /读取数据 b-readpos+; if (b-readpos = BUFFER_SIZE) b-readpos = 0; pthread_cond_signal( /设置状态变量 pthread_mutex_unlock( /释放互斥锁 return data; 4主要的多线程 API 在本程序的代码中大量的使用了线程函数,如 pthread_cond_signal、pthread_mutex_init、pthread_mutex_lock 等等,这些函数的作用是什么,在哪里定义的,我们将在下面的内容中为大家做一个简单的介绍,并且为其中比

13、较重要的函数做一些详细的说明。 线程创建函数: int pthread_create (pthread_t * thread_id, _const pthread_attr_t * _attr, void *(*_start_routine) (void *),void *_restrict _arg) 获得父进程 ID: pthread_t pthread_self (void) 测试两个线程号是否相同: int pthread_equal (pthread_t _thread1, pthread_t _thread2) 线程退出: void pthread_exit (void *_ret

14、val) 等待指定的线程结束: int pthread_join (pthread_t _th, void *_thread_return) 互斥量初始化:pthread_mutex_init (pthread_mutex_t *,_const pthread_mutexattr_t *) 销毁互斥量: int pthread_mutex_destroy (pthread_mutex_t *_mutex) 再试一次获得对互斥量的锁定(非阻塞): int pthread_mutex_trylock (pthread_mutex_t *_mutex) 锁定互斥量(阻塞): int pthread_

15、mutex_lock (pthread_mutex_t *_mutex) 解锁互斥量: int pthread_mutex_unlock (pthread_mutex_t *_mutex) 条件变量初始化: int pthread_cond_init (pthread_cond_t *_restrict _cond, _const pthread_condattr_t *_restrict _cond_attr) 销毁条件变量 COND: int pthread_cond_destroy (pthread_cond_t *_cond) 唤醒线程等待条件变量: int pthread_cond_

16、signal (pthread_cond_t *_cond) 等待条件变量(阻塞): int pthread_cond_wait (pthread_cond_t *_restrict _cond, pthread_mutex_t *_restrict _mutex) 在指定的时间到达前等待条件变量: int pthread_cond_timedwait (pthread_cond_t *_restrict _cond, pthread_mutex_t *_restrict _mutex, _const struct timespec *_restrict _abstime) PTHREAD 库

17、中还有大量的 API 函数,用户可以参考其他相关书籍。下面我们对几个比较重要的函数做一下详细的说明: pthread_create 线程创建函数 int pthread_create (pthread_t * thread_id,_const pthread_attr_t * _attr, void *(*_start_routine) (void *),void *_restrict _arg) 线程创建函数第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread 不需要参数,所以最后一个参数

18、设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。当创建线程成功时,函数返回 0,若不为 0 则说明创建线程失败,常见的错误返回代码为 EAGAIN 和 EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。pthread_join 函数 用来等待一个线程的结束。函数原型为:int pthread_join (pthread_t _th, void *_thread_return) 第一个参数为被等待的线程标识符,第二个参数为一个用户定义

19、的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。 pthread_exit 函数 一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数 pthread_exit 来实现。它的函数原型为: void pthread_exit (void *_retval) 唯一的参数是函数的返回代码,只要 pthread_join 中的第二个参数 thread_return 不是NULL,这个值将被传递给 thread_return。最后要说明的是

20、,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用 pthread_join 的线程则返回错误代码 ESRCH。 下面我们来介绍有关条件变量的内容。使用互斥锁来可实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满

21、足。一般说来,条件变量被用来进行线线程间的同步。 pthread_cond_init 函数 条件变量的结构为 pthread_cond_t,函数 pthread_cond_init()被用来初始化一个条件变量。它的原型为: int pthread_cond_init (pthread_cond_t * cond, _const pthread_condattr_t * cond_attr) 其中 cond 是一个指向结构 pthread_cond_t 的指针,cond_attr 是一个指向结构pthread_condattr_t 的指针。结构 pthread_condattr_t 是条件变量的

22、属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为 pthread_cond_ destroy(pthread_cond_t cond) 。 pthread_cond_wait 函数 使线程阻塞在一个条件变量上。它的函数原型为: extern int pthread_cond_wait (pthread_cond_t *_restrict_cond, pthread_mutex_t *_re

23、strict _mutex) 线程解开 mutex 指向的锁并被条件变量 cond 阻塞。线程可以被函数pthread_cond_signal和函数 pthread_cond_broadcast 唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为 0 等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用 while 语句实现。pthread_cond_timedwait 函数 另一个用来阻塞线程的函数是 pthread_cond_t

24、imedwait() ,它的原型为: extern int pthread_cond_timedwait _P (pthread_cond_t *_cond, pthread_mutex_t *_mutex, _const struct timespec *_abstime) 它比函数 pthread_cond_wait()多了一个时间参数,经历 abstime 段时间后,即使条件变量不满足,阻塞也被解除。 pthread_cond_signal 函数 它的函数原型为: extern int pthread_cond_signal (pthread_cond_t *_cond) 它用来释放被阻

25、塞在条件变量 cond 上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用 pthread_cond_wait 函数之间被发出,从而造成无限制的等待。 六、实验步骤 1、阅读源代及编译应用程序 进入 exp/basic/02_pthread 目录,使用 vi 编辑器或其他编辑器阅读理解源代码。运行make产生 pthread 可执行文件。2、下载和调试 切换到 minicom 终端窗口,使用 NFS mount 开发主机的/arm2410s 到/host 目录。 进入/host/exp/basic/pthread 目录,运行 pthread,观察运行结果的正确性。运行程序最后一部分结果如下: wait for not empty put994 put995 put996 put997 put998 put999 producer stopped! 993get 994get 995get 996get 997get 998get 999get consumer stopped! /host/exp/basic/02_pthread七、思考题 1加入一个新的线程用于处理键盘的输入,并在按键为 ESC 时终止所有线程。 2线程的优先级的控制。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 重点行业资料库 > 1

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。