一、问题描述
生产者-消费者问题是一个经典的进程同步问题,该问题最早由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()
{
//创建各个互斥信号
g_hMutex = CreateMutex(NULL,FALSE,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
DWORD producerID[CONSUMERS_COUNT]//生产者线程的标识符
DWORD consumerID[THREADS_COUNT]//消费者线程的标识符
//创建生产者线程
for (int i=0i<PRODUCERS_COUNT++i){
hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i])
if (hThreads[i]==NULL) return -1
}
//创建消费者线程
for (int i=0i<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
}
//生产一个产品。简单模拟了一下,仅输出新产品的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=0i<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=0i<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_hFullSemaphore,INFINITE)
WaitForSingleObject(g_hMutex,INFINITE)
Produce()
Append()
Sleep(1500)
ReleaseMutex(g_hMutex)
ReleaseSemaphore(g_hEmptySemaphore,1,NULL)
}
return 0
}
//消费者
DWORD WINAPI Consumer(LPVOID lpPara)
{
while(g_continue){
WaitForSingleObject(g_hEmptySemaphore,INFINITE)
WaitForSingleObject(g_hMutex,INFINITE)
Take()
Consume()
Sleep(1500)
ReleaseMutex(g_hMutex)
ReleaseSemaphore(g_hFullSemaphore,1,NULL)
}
return 0
}
你上面列举的是读者写者问题,是互斥量,意思是只有获得互斥信号才能继续执行。1、rmutex与wmutex的初始值均为1,wait一次就减一,signal一次就加一,其值只能是1或者是0
2、就拿读者来说吧(rmutex就是reader mutex),读者要去读的话必须得获得rmutex,那么就得wait(rmutex),
3、等有rmutex时,才准许你进门,等你进门了,你就不需要rmutex了(说白了,rmutex就是进出门的门卡,只有一张,进出都必须是一次一个人),所以你就释放它吧,那么就signal(rmutex),就是提醒后面的读者看哥不用这玩意儿了,你们爱咋咋用去,就扔给他们了,后面的读者看到有rmutex了也会做相同的步骤。出去的时候同样要等待门卡,没门卡出不去啊,也是wait和signal *** 作。
4、上面没有说明wmutex,其实在读者进去的时候要拿这个进去,wmutex可以理解为写好了的书,进去后你要拿着新写好的书看,如果没有新写好的,等吧,就是wait(wmutex),等获得新书的时候就进去看,等你看完的时候就把书扔在门口就是signal(wmutex)并说老子看完了,赶紧给老子写去,等待的写者看到有wmutex可以用的时候就屁颠屁颠儿的拿去写去了,那就是另一个写过程了。
读写锁与互斥量类似,不过读写锁的并行性更高。读写锁可以有三种状态:(1)读模式加锁;(2)写模式加锁;(3)不加锁。
在写加锁状态时,在解锁之前,所有试图对这个锁加锁的线程都会被阻塞。在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权限。但是如果线程希望以写模式加锁,它必须阻塞,直至所有的线程释放读锁。
读写锁很适合于对数据结构读的次数远大于写的情况。
相关函数:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr)
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) // 成功则返回0,失败则返回错误代码
int pthread_rwlock_rdlock(pthread_rwlock_t *restrict rwlock) //读模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *restrict rwlock)//写模式加锁
int pthread_rwlock_unlock(pthread_rwlock_t *restrick rwlock)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *restrict rwlock)
int pthread_rwlock_trywrlock(pthread_rwlock_t *restrict rwlock)
int pthread_rwlock_trywrlock(pthread_rwlock_t *restrict rwlock)
相关示例:读者写者问题,这也是一个很经典的多线程题目,题目大意:有一个写者多个读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读取文件,同样有读者读文件时
#include <stdio.h>
#include <pthread.h>
#define Read_Num 2
pthread_rwlock_t lock
class Data
{
public:
Data(int i, float f): I(i),F(f)
{}
private:
int I
float F
}
Data *pdata = NULL
void *read(void * arg)
{
int id = (int)arg
while(true)
{
pthread_rwlock_rdlock(&lock)
printf(" reader %d is reading data!\n", id)
if(data == NULL)
{
printf("data is NULL\n")
}
else
{
printf("data: I = %d, F = %f \n", pdata->I, pdata->F)
}
pthread_rwlock_unlock(&lock)
}
pthread_exit(0)
}
void *write()
{
while(true)
{
pthread_rwlock_wrlock(&lock)
printf(" writer is writind data!\n")
if(pdata == NULL)
{
pdata = new Data(1, 1.1)
printf("Writer is writing data: %d, %f\n", pdata->I, pdata->F)
}
else
{
delete pdata
pdata = NULL
printf("writer free the data!")
}
pthread_rwlock_unlock(&lock)
}
pthread_exit(0)
}
void main()
{
pthread_t reader[Read_Num]
pthread_t writer
for(int i = 0i<Read_Numi++)
{
pthread_create(&read[i],NULL,read,(void *)i)
}
pthread_create(writer, NULL, write, NULL)
sleep(1)
return 0
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)