生产者与消费者模型的实现

生产者与消费者模型的实现

当我们学习完了信号量和共享内存之后,我们就可以实现进程间的同步和互斥了。然后其中最经典的例子就是生产消费者模型了。
现在就和大家一起分析下生产消费者模型

以下代码是一个多个生产者和多个消费者对N个缓冲区进行访问的例子。首先先看看我们的伪代码

//生产者:
while (1)
{
	p(semid, 1);
	sleep(3);
	p(semid, 0);
	//producer is producing a product
	goods = rand() % 10;//product a goods
	shmaddr[indexaddr[0]] = goods;//The goods is placed on a shelf
	printf("producer:%d produces a product[%d]:%d\n", getpid(), indexaddr[0], goods);
	indexaddr[0] = (indexaddr[0] + 1) % 10;
	v(semid, 0);
	sleep(3);
	v(semid, 2);
}
//消费者:
while (1)
{
	p(semid, 2);
	sleep(1);
	p(semid, 0);
	//consumer is consuming a product
	goods = shmaddr[indexaddr[1]];//The goods on the shelf is taken down
	printf("consumer:%d consumes a product[%d]:%d\n", getpid(), indexaddr[1], goods);
	indexaddr[1] = (indexaddr[1] + 1) % num;
	v(semid, 0);
	sleep(1);
	v(semid, 1);
}

上面的代码和课本上的不一样,因为上面的代码是由伪代码的linux C语言实现的。其中的p(semid,0)和v(semid,0)的作用是让进程互斥访问临界区。临界区中包含的数据indexaddr[0],indexaddr[1],以及shmaddr数组分别对应伪代码中的in,out,buffer。p(semid,1)和v(semid,2)以及p(semid,2)和v(semid,1)实现的是同步作用。

并且,在生产者中,生产者生产了一个货物(goods=rand()%10;),然后将这个货物放上货架(shmaddr[indexaddr[0]]=goods;)。在消费者中,消费和从货架上取下货物(goods=shmaddr[indexaddr[1]];)。

好了,现在再看一边上面的代码,我想你的思路就清晰了。

了解了核心代码,并不能算就完成了生产者和消费者模型,因为生产者和消费者核心代码前还得做一些些准备工作,具体要准备些什么,我们具体来分析。

首先申请一块共享内存,这块共享内存用于存放生产者所生产的货物。同时我们可以看到这块共享内存大小为10字节。这里需要注意,每个生产着或消费者运行后,都要去试着分配这样的一块共享内存。如果在当前进程运行前已经有某个进程已经创建了这块共享内存,那么这个进程就不再创建(此时createshm会返回-1并且错误代码为EEXIST),只是打开这块共享内存。创建后,再将这块共享内存添加到当前进程的地址空间。

num = 10;
//create a shared memory as goods buffer
if ((shmid_goods = createshm(".", 's', num)) == -1)
{
	if (errno == EEXIST)
	{
		if ((shmid_goods = openshm(".", 's')) == -1)
		{
			exit(1);
		}
	}
	else
	{
		perror("create shared memory failed\n");
		exit(1);
	}
}
//attach the shared memory to the current process
if ((shmaddr = shmat(shmid_goods, (char*)0, 0)) == (char*)-1)
{
	perror("attach shared memory error\n");
	exit(1);
}

接下来还要再申请一块共享内存,用于存放两个整形变量in和out(其实就是申请一个含有2个整形变量的数组而已)。他们记录的是生产和消费货物时“货架”的索引。与上面情况相同,如果已经有其他进程创建了此块共享内存,那么当前进程只是打开它而已。

注意这里对两个整形变量的初始化时的值均为0。

//create a shared memory as index
if ((shmid_index = createshm(".", 'z', 8)) == -1)
{
	if (errno == EEXIST)
	{
		if ((shmid_index = openshm(".", 'z')) == -1)
		{
			exit(1);
		}
	}
	else
	{
		perror("create shared memory failed\n");
		exit(1);
	}
}
else
{
	is_noexist = 1;
}
//attach the shared memory to the current process
if ((indexaddr = shmat(shmid_index, (int*)0, 0)) == (int*)-1)
{
	perror("attach shared memory error\n");
	exit(1);
}
if (is_noexist)
{
	indexaddr[0] = 0;
	indexaddr[1] = 0;
}

接下来就是创建一个信号量集,这个信号量集中包含三个信号量。第一个信号量实现的互斥作用,即进程对临界区的互斥访问。剩下两个均实现的是同步作用,协调生产者和消费者的合理运行,即货架上没有空位时候生产者不再生产,货架上无商品时消费者不再消费。

注意下面对每个信号量的赋值情况。互斥信号量当然初值为1。而同步信号量两者之和不能大于num的值。

//create a semaphore set including 3 semaphores
if ((semid = createsem(".", 't', 3, 0)) == -1)
{
	if (errno == EEXIST)
	{
		if ((semid = opensem(".", 't')) == -1)
		{
			exit(1);
		}
	}
	else
	{
		perror("semget error:");
		exit(1);
	}
}
else
{
	union semun arg;
	//seting value for mutex semaphore
	arg.val = 1;
	if (semctl(semid, 0, SETVAL, arg) == -1)
	{
		perror("setting semaphore value failed\n");
		return -1;
	}
	//set value for synchronous semaphore
	arg.val = num;
	//the num means that the producer can continue to produce num products
	if (semctl(semid, 1, SETVAL, arg) == -1)
	{
		perror("setting semaphore value failed\n");
		return -1;
	}
	//the last semaphore's value is default
	//the default value '0' means that the consumer is not use any product now
}

基本上这样,就算完成了生产者和消费者的前期工作。我们可以看到,在核心代码中,我们只需要“装模作样”的将代码“各就各位”即可,当然这需要你理解生产者消费者这个基本模型。而在下面的准备代码中,则需要我们理解关于信号量和共享内存的一些基本函数。

最后再说说使用,建议先运行一个生产者和一个消费者,观察两者是如何协调工作的。然后再只运行一个生产者或一个消费者,看其是否会阻塞。了解了以上情况后,你就可以同时运行多个生产者和消费者了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个使用 Qt 线程池实现生产者消费者模型的简单示例: ```cpp #include <QtCore> #include <iostream> class Producer : public QObject { Q_OBJECT public: Producer(QThreadPool *pool, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_pool(pool), m_mutex(mutex), m_cond(cond), m_data(data) { m_count = 0; } public slots: void produce() { int num = qrand() % 100; QRunnable *task = new Task(num, m_mutex, m_cond, m_data); m_pool->start(task); m_count++; std::cout << "Produced " << num << std::endl; m_cond->wakeAll(); } int count() const { return m_count; } private: QThreadPool *m_pool; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; int m_count; }; class Consumer : public QObject { Q_OBJECT public: Consumer(QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_mutex(mutex), m_cond(cond), m_data(data) { } public slots: void consume() { m_mutex->lock(); while (m_data->isEmpty()) { m_cond->wait(m_mutex); } int num = m_data->takeFirst(); std::cout << "Consumed " << num << std::endl; m_mutex->unlock(); } private: QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; class Task : public QRunnable { public: Task(int num, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_num(num), m_mutex(mutex), m_cond(cond), m_data(data) { } void run() { m_mutex->lock(); while (m_data->size() >= 10) { m_cond->wait(m_mutex); } m_data->append(m_num); m_mutex->unlock(); } private: int m_num; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qsrand(QTime::currentTime().msec()); QThreadPool pool; pool.setMaxThreadCount(5); QMutex mutex; QWaitCondition cond; QList<int> data; Producer producer(&pool, &mutex, &cond, &data); Consumer consumer(&mutex, &cond, &data); QObject::connect(&producer, &Producer::produce, &consumer, &Consumer::consume); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &producer, &Producer::produce); timer.start(1000); QTimer countTimer; QObject::connect(&countTimer, &QTimer::timeout, [&producer]() { std::cout << "Produced " << producer.count() << " items" << std::endl; }); countTimer.start(5000); return app.exec(); } ``` 在上面的示例中,`Producer` 和 `Consumer` 类继承自 `QObject`,并使用 Qt 信号和槽机制进行通信。`Producer` 类的 `produce()` ### 回答2: Qt线程池可以很好地实现生产者消费者模型生产者消费者模型主要是通过一个共享的缓冲区实现生产者生产数据,消费者消费数据的过程。下面是一个使用Qt线程池实现生产者消费者模型的示例: 1. 定义一个数据结构,用于存储生产者消费者共享的数据。这个数据结构可以是一个队列或者一个列表。 2. 创建一个Qt线程池,并设置线程池的最大线程数。 3. 创建生产者线程和消费者线程,并将它们添加到线程池中。 4. 在生产者线程中,生产数据并将数据添加到共享的数据结构中。可以使用Qt的信号槽机制,在生产者线程中发送信号,将数据发送给消费者线程。 5. 在消费者线程中,从共享的数据结构中获取数据并进行消费。可以使用Qt的信号槽机制,在消费者线程中接收信号,并处理收到的数据。 6. 在主线程中,等待生产者线程和消费者线程完成工作,并关闭线程池。 通过使用Qt线程池,可以方便地管理多个线程,避免手动管理线程的创建和销毁,从而简化了生产者消费者模型实现。此外,Qt的信号槽机制可以方便地实现线程间的通信,从而实现生产者消费者之间的数据传递。 ### 回答3: Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的类库和工具,其中也包括线程池的实现。在Qt中,可以使用QThreadPool类来创建和管理线程池。 生产者消费者模型是一种常见的多线程编程模型,其中有一组线程作为生产者,负责生成数据,另一组线程作为消费者,负责处理这些数据。线程池可以很好地支持这种模型,并提供了以下步骤来实现: 1. 创建一个线程池对象:使用QThreadPool类创建一个线程池对象,设置最大线程数、线程闲置时间等属性。 2. 创建生产者线程:通过继承QRunnable类,实现自己的生产者线程类。在类中重写run()函数,在其中完成需要生产的数据的生成。 3. 创建消费者线程:同样通过继承QRunnable类,实现自己的消费者线程类。在类中重写run()函数,在其中完成对生产者生成的数据的处理。 4. 将任务添加到线程池:使用QThreadPool的start()函数将生产者消费者线程对象添加到线程池中,线程池会自动分配线程去运行这些任务。 5. 等待线程池完成任务:可以使用QThreadPool的waitForDone()函数来等待线程池中的所有任务完成,确保所有生产者消费者线程都执行完毕。 通过以上步骤,我们可以在Qt中实现简单的生产者消费者模型。线程池可以很好地管理线程的创建和销毁,提高线程利用率和系统的性能。同时,Qt的线程池也提供了一些其他的功能,比如任务优先级和取消线程等,可以根据实际需求来灵活调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值