多线程

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第三讲多线程程序设计嵌入式系统程序与硬件紧密相关,对程序实时性要求很高。通常一个程序不会只做一件事,如果程序在处理按键操作的时候,只能盲目等待,一方面浪费了宝贵的系统时间和资源,更可怕的是会导致其他事件无法被及时处理。如果能让程序同时可以做N件事,那么就不会出现上面的情况了。多线程编程方法可以让程序“同时做N件事”。这里所说的同时并不是真正意义上的同时,因为一个处理器核心同时只能做一件事。它只是让人感觉是同时在做N件事,具体的处理方法由操作系统完成,程序员不用过多参与。一种比较典型的处理方法是“时间片原则”。在编写多线程程序的时候,程序员要注意满足存储器,寄存器、外设等资源的访问约定,因为很多资源是要求“互斥访问”的。以及不同线程之间的相互制约关系。合理使用多线程方法,能让程序员回避一些复杂的逻辑关系问题,使编程变得容易。问题有一辆车,司机可以开车、停车;乘客需要上车、下车;上下车人数也都事先定好了。如果是火车,一般都是调度中心统一指挥,指定时间开车停车,乘客在指定地点上车下车。如果是公共汽车,再用这种统一、固定模式的调度方法就很不科学了。汽车的到站时间不像火车那么精确,乘客的数量变化也比较明显,如果再统一调度,肯定会出现没人也停车,或者人没上完车就开了或者跑空车等等情况。通常的做法是到站停车让乘客上,上满让乘客等下趟;如果空车,前方到站又没人,干脆不停了。再便民一点的小公共,就是招手停。经典多线程案例----生产者消费者问题程序总体结构初始化函数(二级)向缓冲区里放一个数(三级)从缓冲区里取一个数(三级)生产者线程的函数(二级)消费者线程的函数(二级)main(一级)头文件、变量和主函数#include“pthread.h“/*多线程函数头文件*/#defineBUFFER_SIZE16/*设置一个整数的圆形缓冲区*/structprodcons{intbuffer[BUFFER_SIZE];/*缓冲区数组*/pthread_mutex_tlock;/*互斥锁*/intreadpos,writepos;/*读写的位置*/pthread_cond_tnotempty;/*缓冲区非空信号*/pthread_cond_tnotfull;/*缓冲区非满信号*/}buffer/*结构型变量buffer*/intmain(void)/*主函数*/{pthread_tth_a,th_b;void*retval;/*存储被等待线程的返回值*/init(&buffer);pthread_create(&th_a,NULL,producer,0);pthread_create(&th_b,NULL,consumer,0);/*等待生产者和消费者结束*/pthread_join(th_a,&retval);pthread_join(th_b,&retval);return0;}二级初始化、生产者和消费者/*初始化缓冲区*/voidinit(structprodcons*b){pthread_mutex_init(&b-lock,NULL);/*初始化后处于解锁状态*/pthread_cond_init(&b-notempty,NULL);/*指针为空时默认状态*/pthread_cond_init(&b-notfull,NULL);b-readpos=0;b-writepos=0;}/*生产者进程函数*/void*producer(void*data){intn;for(n=0;n1000;n++){printf(put--%d\n,n);put(&buffer,n);}put(&buffer,OVER);printf(producerstopped!\n);returnNULL;}/*消费者进程函数*/void*consumer(void*data){intd;while(1){d=get(&buffer);if(d==OVER)break;printf(%d--get\n,d);}printf(consumerstopped!\n);returnNULL;}三级向公共缓冲区存取数据/*向缓冲区中写入一个整数*/voidput(structprodcons*b,intdata){pthread_mutex_lock(&b-lock);/*获得互斥锁*//*等待缓冲区非满*/while((b-writepos+1)%BUFFER_SIZE==b-readpos){printf(waitfornotfull\n);pthread_cond_wait(&b-notfull,&b-lock);/*条件变量阻塞*/}/*写数据并且指针前移*/b-buffer[b-writepos]=data;b-writepos++;if(b-writepos=BUFFER_SIZE)b-writepos=0;/*设置缓冲区非空信号*/pthread_cond_signal(&b-notempty);/*条件变量唤醒*/pthread_mutex_unlock(&b-lock);/*互斥锁解锁*/}/*endput*//*从缓冲区中读出一个整数*/intget(structprodcons*b){intdata;pthread_mutex_lock(&b-lock);/*获得互斥锁*//*等待缓冲区非空*/while(b-writepos==b-readpos){printf(waitfornotempty\n);pthread_cond_wait(&b-notempty,&b-lock);/*条件变量阻塞*/}/*读数据并且指针前移*/data=b-buffer[b-readpos];b-readpos++;if(b-readpos=BUFFER_SIZE)b-readpos=0;/*设置缓冲区非满信号*/pthread_cond_signal(&b-notfull);/*条件变量唤醒*/pthread_mutex_unlock(&b-lock);/*互斥锁解锁*/returndata;}pthread_create线程创建函数intpthread_create(pthread_t*thread_id,__constpthread_attr_t*__attr,void*(*__start_routine)(void*),void*__restrict__arg)pthread_create(&th_a,NULL,producer,0);第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。pthread_join函数用来等待一个线程的结束。intpthread_join(pthread_t__th,void**__thread_return)第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。pthread_join(th_a,&retval);pthread_cond_wait函数使线程阻塞在一个条件变量上。externintpthread_cond_wait(pthread_cond_t*__restrict__cond,pthread_mutex_t*__restrict__mutex)线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。pthread_cond_signal函数externintpthread_cond_signal(pthread_cond_t*__cond)它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。思考题1修改例程。两个生产者线程,一个向缓冲区写入0~299之间的数,另一个向缓冲区写入1000~1299之间的数;一个消费者线程读取所有的数。2加入一个新的线程用于处理键盘的输入,并在按键为ESC时终止所有线程。32个生产者线程和3个消费者线程。共享缓冲区有16个存储空间。生产者a1写入0~999之间的数,生产者a2写入1000~1999之间的数。消费者b1读取素数,消费者b2读取和数,消费者b3读取百位是奇数的数。

1 / 25
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功