1.实验目的
本实验的目的是通过编写和调试一个解决生产者-消费者问题的简单模拟程序,进一步深入理解课堂教学中讲授的进程同步问题,以及用于解决同步问题的信号量机制的基本思想,即通过研究进程同步和信号量机制实现生产者消费者问题的并发控制,以便阶段性的巩固学习成果。
2.实验内容以及概述
(1)概述
说明:有界缓冲区内设有20个存储单元,放入/取出的数据项设定为1~20这20个整型数。
编制程序模拟解决生产者-消费者同步问题。具体设计要求:
1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容,当前指针位置和生产者/消费线程的标识符。
2)生产者和消费者各有两个以上
3)多个生产者或多个消费者之间须有共享对缓冲区进行操作的函数代码
(2)设计原理
通过一个有界缓冲区把生产者和消费者联系起来。假定生产者和消费者的优先级是相同的,只要缓冲区未满,生产者就可以生产产品并将产品送入缓冲区。类似地,只要缓冲区未空,消费者就可以从缓冲区取走产品。应该禁止生产者向未满的缓冲区送入产品,同时也应该禁止消费者从空的缓冲区取走产品,在这一机制下由生产者线程和消费者线程之间的互斥关系来实现。与计算打印两进程同步关系不同,生产者和消费者两进程producer和consumer之间应满足下列两个同步条件:
a)只有在缓冲区中至少有一个缓冲区已存入消息后,消费者才能从中提取信息,否则 消费者必须等待
b)只有缓冲池中至少有一个缓冲区是空时,生产者才能把信息放入缓冲区,否则生产者必须等待。
为了满足第一个同步条件,设置一个同步信号量full,它代表的是缓冲区满,它的初始值为0,它的值为n时整个 缓冲池满。这个资源时消费者进程consumer所有,consumer进程可以申请该资源,对它施加p操作,而consumer进程的合作进程producer对它施加v操作。同样为了满足第二个同步条件,设置另一个同步信号量empty,它代表资源是缓冲空区,它的初始值为n,表示缓冲池中所有缓冲区空。信号量full表示可用缓冲区数量,信号量empty表示缓冲区数量,设置整形变量:存入指针in和取出指针out。
为了解决生产者和消费者问题,应该设置两个资源信号量,其中一个表示空缓冲区的数目,用g_hEmptySemaphore表示,其初始值为有界缓冲区的大小SIZE_OF_BUFFER;另一个表示缓冲区中产品的数目,用g_hFullSemaphore表示,其初始值为0。另外,由于有界缓冲区是一个临界资源,必须互斥使用,所以还需要在设置一个互斥信号量g_hMutex,其初始值为1.
p原语的主要动作是:
a)sem(信号量)减1;
b)若sem减一后仍大于或等于零,则进程继续进行;
c)若进程减一后小于零,则该进程被阻塞后与该信号相对应的队列中,然后转进程调度。
v原语的主要操作是:
a)sem加一;
b)若相加结果大于零,进程继续进行;
c)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程然后在返回原进程继续执行或转进程调度。
采用的同步方法:
a)利用函数CreateMutex(NULL,FALSE,NULL)创建互斥信号量g_hMutex,表示缓冲区当前的状态,若为true时,则表示缓冲区正被别的进程使用。三个参数表示的意义是:指向安全属性的指针,初始化互斥对象的所有者,指向互斥对象名的指针。
b)利用函数CreateSemaphore(NULL,SIZE_OF_BUFFER,NULL)创建缓冲区空的信号量g_hEmptySemaphore,值为true时表示缓冲区已满。四个参数分别为:表示是否允许继承.设置信号机构的初始计数.设置信号机的最大计数.指定信号机对象的名称(-1是因为计数从开始)。
c)利用函数CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL)创建缓冲区满的信号量g_hFullSemaphore,该值为true时表示缓冲区为空。
#include <iostream>
unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标
unsigned short out = 0; //产品出缓冲区时的缓冲区下标
bool g_continue = true; //控制程序结束
HANDLE g_hMutex; //用于线程间的互斥
HANDLE g_hFullSemaphore; //当缓冲区空时迫使消费者等待
HANDLE g_hEmptySemaphore; //当缓冲区满时迫使生产者等待
DWORD WINAPI Consumer(LPVOID); //消费者线程
{
g_hMutex = CreateMutex(NULL, FALSE, NULL);
//加注释,说明上面函数的功能,各参数的含义
g_hEmptySemaphore = CreateSemaphore(NULL, SIZE_OF_BUFFER, SIZE_OF_BUFFER, NULL);
//加注释,说明上面函数的功能,各参数的含义
const unsigned short PRODUCERS_COUNT = 3; //生产者的个数
const unsigned short CONSUMERS_COUNT = 1; //消费者的个数
const unsigned short THREADS_COUNT = PRODUCERS_COUNT + CONSUMERS_COUNT; //总的进程数
DWORD producerID[PRODUCERS_COUNT]; //生产者线程的标识符
DWORD consumerID[CONSUMERS_COUNT]; //消费者线程的标识符
//加注释,说明该For循环的作用
for (int i = 0; i<PRODUCERS_COUNT; ++i) {
hThreads[i] = CreateThread(NULL, 0, Producer, NULL, 0, &producerID[i]);
//加注释,说明上面函数的功能,各参数的含义
if (hThreads[i] == NULL) return -1;
}
//加注释,说明该For循环的作用
for (int i = 0; i<CONSUMERS_COUNT; ++i) {
hThreads[PRODUCERS_COUNT + i] = CreateThread(NULL, 0, Consumer, NULL, 0, &consumerID[i]);
if (hThreads[i] == NULL) return -1;
}
if (getchar()) { //按回车后终止程序运行
g_continue = false;
}
}
return 0;
}
void Produce()
{
std::cerr << "Producing " << ++ProductID << " ... ";
std::cerr << "Succeed" << std::endl;
}
void Append()
{
std::cerr << "Appending a product ... ";
g_buffer[in] = ProductID;
in = (in + 1) % SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
for (int i = 0; i<SIZE_OF_BUFFER; ++i) {
std::cout << i << ": " << g_buffer[i];
if (i == in) std::cout << " <-- 生产";
if (i == out) std::cout << " <-- 消费";
std::cout << std::endl;
}
}
void Take()
{
std::cerr << "Taking a product ... ";
ConsumeID = g_buffer[out];
out = (out + 1) % SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
for (int i = 0; i<SIZE_OF_BUFFER; ++i) {
std::cout << i << ": " << g_buffer[i];
if (i == in) std::cout << " <-- 生产";
if (i == out) std::cout << " <-- 消费";
std::cout << std::endl;
}
}
//加注释,说明下面函数的功能
void Consume()
{
std::cerr << "Consuming " << ConsumeID << " ... ";
std::cerr << "Succeed" << std::endl;
}
DWORD WINAPI Producer(LPVOID lpPara)
{
while (g_continue) {
WaitForSingleObject(g_hEmptySemaphore, INFINITE);
//加注释,说明上面函数的功能,各参数的含义
WaitForSingleObject(g_hMutex, INFINITE);
Produce();
Append();
Sleep(1500);
//加注释,说明上面函数的功能,各参数的含义
ReleaseSemaphore(g_hFullSemaphore, 1, NULL);
//加注释,说明上面函数的功能,各参数的含义
return 0;
}
DWORD WINAPI Consumer(LPVOID lpPara)
{
while (g_continue) {
WaitForSingleObject(g_hFullSemaphore, INFINITE);
WaitForSingleObject(g_hMutex, INFINITE);
Take();
Consume();
Sleep(1500);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hEmptySemaphore, 1, NULL);
}
return 0;
}