1. 算法思想
因为有多个缓冲区,所以生产者线程没有必要在生成新的数据之前等待最后一个数据被消费者线程处理完毕。同样,消费者线程并不一定每次只能处理一个数据。在多缓冲区机制下,线程之间不必互相等待形成死锁,因而提高了效率。 多个缓冲区就好像使用一条传送带替代托架,传送带上一次可以放多个产品。生产者在缓冲区尾 加入数据,而消费者则在缓冲区头读取数据。当缓冲区满的时候, 缓冲区就上锁并等待消费者线程读取数据;每一个生产或消费动作使得传送带向前移动一个单位,因而,消费者线程读取数据的顺序和数据产生顺序是相同的。 可 以引入一个 count计数器来表示已经被使用的缓冲区数量。用 hNotEmptyEvent 和 hNotFullEvent信号量来同步生产者和消费者线程。每当生产者线程发现缓冲区满( count=BufferSize ),它就等待 hNotEmptyEvent 事件。同样,当消费者线程发现缓冲区空,它就开始等待 hNotEmptyEvent。生产者线程写入一个新的数据之后,就立刻发出 hNotEmptyEvent 来唤醒正在等待的消费者线程;消费者线程在读取一个数据之后,就发出 hNotFullEvent 来唤醒正在等待的生产者线程。 程序的设计思想大致为:设置一 while循环, pi生产者访问临界区,得到权限访问缓冲区,如果缓冲区满 的,则等待,直到缓冲区非满;访问互斥锁,当得到互斥锁 且 缓冲区非满时,跳出 while循环,开始产生新数据,并把数据存放于 Buffer缓冲区中,当数 据存放结束则结束临界区;接着唤醒消费者线程; ci消费者访问临界区,得到权限访问缓冲区,如果缓冲区为空,没有可以处理的数据,则释放互斥锁且等待,直 到缓冲区非空;当等到缓冲区非空时,跳出 while循环;消费者获得数据,并根据所获得的数据按类别消费(当消费者获得的数据为大写字母时,则把大写字母 转换成小写字母,并显示;当消费者获得的数据为小写字母时,则把小写字母转换成大写字母,并显示;当消费者获得的数据为字符 0、 1、 2、 ……8、 9时,把 这些字符直接显示到屏幕;当消费者获得的数据为符号(+、-、 *、 \……)时,把这些符号打印成 7行 7列的菱形);处理完数据后,结束临界区;接着唤醒生 产者线程。
另外,由于有界缓冲区是一个临界资源,必须互斥使用,所以还需要再设置一个互斥信号量 g_hMutex,起初值为 1 。
2 算法实现
生 产者 -消费者问题是一个经典的进程同步问题,该问题最早由 Dijkstra提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程。生 产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有 空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物 品被生产出来。
#include <windows.h> #include <iostream> const unsigned short SIZE_OF_BUFFER = 10; // 缓冲区长度 unsigned short ProductID = 0; // 产品号 unsigned short ConsumeID = 0; // 将被消耗的产品号 unsigned short in = 0; // 产品进缓冲区时的缓冲区下标 unsigned short out = 0; // 产品出缓冲区时的缓冲区下标 int g_buffer[SIZE_OF_BUFFER]; // 缓冲区是个循环队列 bool g_continue = true; // 控制程序结束 HANDLE g_hMutex; // 用于线程间的互斥 保护缓冲区临界资源 HANDLE g_hFullSemaphore; // 当缓冲区满时迫使生产者等待 HANDLE g_hEmptySemaphore; // 当缓冲区空时迫使消费者等待 DWORD WINAPI Producer(LPVOID); // 生产者线程 DWORD WINAPI Consumer(LPVOID); // 消费者线程
int main() { // Create mutex and semophores g_hMutex = CreateMutex(NULL,FALSE,NULL); g_hFullSemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER-1 ,SIZE_OF_BUFFER-1,NULL); g_hEmptySemaphore = CreateSemaphore(NULL,0 ,SIZE_OF_BUFFER-1,NULL); // 调整下面的数值,可以发现,当生产者个数多于消费者个数时, // 生产速度快,生产者经常等待消费者;反之,消费者经常等待 const unsigned short PRODUCERS_COUNT = 3; // 生产者的个数 const unsigned short CONSUMERS_COUNT = 1; // 消费者的个数 // 总的线程数 const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT; HANDLE hThreads[PRODUCERS_COUNT]; // 各线程的 handle DWORD producerID[CONSUMERS_COUNT]; // 生产者线程的标识符 DWORD consumerID[THREADS_COUNT]; // 消费者线程的标识符 // 创建生产者线程 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 (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; } while(g_continue){ if(getchar()){ // 按回车后终止程序运行 g_continue = false; } } return 0; }
/**********
* 生产者
*/ DWORD WINAPI Producer (LPVOID lpPara) { while (g_continue){ WaitForSingleObject(g_hFullSemaphore,INFINITE); // if g_hFullSemaphore= 0, means the buffer is full, then we have to wait WaitForSingleObject(g_hMutex,INFINITE); // protect the cirtical section of buffer. Produce(); Append(); Sleep(1500); ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hEmptySemaphore,1,NULL); // tell consumer that buffer is no empty, since g_hEmptySemaphore=1 } return 0; }
/**********
* 消费者
*/ DWORD WINAPI Consumer (LPVOID lpPara) { while(g_continue){
// if g_hEmptySemaphore=0, means there is no resource in buffer, we have to wait WaitForSingleObject(g_hEmptySemaphore,INFINITE);
WaitForSingleObject(g_hMutex,INFINITE); Take(); Consume(); Sleep(1500); ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hFullSemaphore,1,NULL); // tell producer that buffer is not full, he can continue to produce. } return 0; }
/*******
* 生产一个产品。简单模拟了一下,仅输出新产品的 ID 号
*/ 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; }
转载于:https://blog.51cto.com/acheng210/530962