费掉。然后对互斥信号量Mutex和资源信号量Full进行V操作,释放资源。
如果缓冲区中已经没有可用资源,就把申请资源的进程添加到等待队列的队尾。如果有一个资源被释放,在等待队列中的第一个进程被唤醒并取得这个资源的使用权。
5.2、程序流程图
生产者线程开始 未通过 资源信号量P操作 通过 互斥信号量P操作 通过 未通过 线程自我阻塞 生产一个产品 线程自我阻塞 把产品送入缓冲区 添加到等待队列 互斥信号量V操作 添加到等待队列 Y 等待队列中有消费者线程 N 资源信号量V操作 唤醒对头的消费者线程 Y 等待队列中有消费者线程 N 唤醒对头的消费者线程 生产者线程结束 图一 生产者流程结构
6
消费者线程开始 未通过 资源信号量P操作 通过 互斥信号量P操作 通过 未通过 线程自我阻塞 从缓冲区取出一个产品 线程自我阻塞 消费一个产品 添加到等待队列 互斥信号量V操作 添加到等待队列 Y 等待队列中有生产者线程 N 资源信号量V操作 唤醒对头的生产者线程 Y 等待队列中有生产者线程 N 唤醒对头的生产者线程 消费者线程结束
图二 消费者流程结构
5.3、程序自定义函数
1、void produce(struct sem_info * );
这个函数是生产者进行的生产过程,为所有的生产者所共享。结构体指针用来接收生产者线程创建时传来的生产者的个人信息。 2、void consumer(struct sem_info * );
这个函数是消费者进行的生产过程,为所有的消费者所共享。结构体指
7
针用来接收消费者线程创建时传来的消费者的个人信息。 3、void setproduce(void);
这个函数是用来设置生产者的个数和他们的名字。 4、void setconsumer(void);
这个函数是用来设置消费者的个数和他们的名字。 5、void activepthread(int);
这个函数是用来创建生产者线程,int型参数为生产者的个数。 6、void activecthread(int);
这个函数是用来创建生产者线程,int型参数为生产者的个数。 7、int gettime(void);
这个函数返回来一个整数,作为线程的sleep()函数的参数。 8、void myscanf(void);
这个函数用来获取设置生产者和消费者的个数时的整数,确保这个数字在0到MAX_BUFFER之间。
5.4、系统函数调用
线程
Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork。
函数pthread_create用来创建一个线程,它的原型为:
extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
void *(*__start_routine) (void *), void *__arg));
第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。第二个参数我们也设为空指针,这样将生成默认属性的线程。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。 函数pthread_join用来等待一个线程的结束。函数原型为: extern
int
pthread_join
__P
((pthread_t
__th,
void
**__thread_return));
第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它
8
可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。 信号量
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数
sem_wait()的非阻塞版本。它们都在头文件
/usr/include/semaphore.h中定义。
信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。 函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
函数sem_destroy(sem_t *sem)用来释放信号量sem。
六.源程序清单
9
6.1、源程序
#include
typedef HANDLE Semaphore; // 信号量的Windows原型 #define P(S) WaitForSingleObject(S,INFINITE) // 定义Windows下的P操作 #define V(S) ReleaseSemaphore(S,1,NULL) // 定义Windows下的V操作
#define rate 1000
#define CONSUMER_NUM 2 /* 消费者个数 */ #define PRODUCER_NUM 3 /* 生产者个数 */ #define BUFFER_NUM 20 /* 缓冲区个数 */
char *thing[8] = {\鸡腿堡\, \薯条\, \可乐\, \三明治\, \面包\, \小笼包\, \火腿\,\馒头\}; //生产和消费的产品名称 struct Buffer {
int product[BUFFER_NUM]; // 缓冲区
int start,end; // 两个指针相当于教材中的 in out 指针 }g_buf;
Semaphore Empty,Full,Mutex; //分别相当于Empty, Full, Mutex三个信号量
/************** 消费者线程*****************************/ DWORD WINAPI Consumer(LPVOID para) {
// i表示第i个消费者
int i = *(int *)para; //利用para传入当前消费者的编号 int ptr; // 待消费的内容的指针 printf(\消费者: 需要资源\\n\, i); int j=0;
while (j++<4){ // 等待产品 P(Full);
// 有产品,先锁住缓冲区 P(Mutex);
// 记录消费的物品 ptr=g_buf.start;
// 再移动缓冲区指针
g_buf.start= (g_buf.start+1)%BUFFER_NUM;
10
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库消费者生产者问题 计算机操作系统课程设计(2)在线全文阅读。
相关推荐: