生产者/消费者的特例

时间:2011-09-01 02:18:25

标签: ruby multithreading algorithm design-patterns

我正在尝试同步一种特殊的生产者/消费者问题。 这就是问题所在:

我有2个队列link_queue, page_queue

线程class ProducePages_RequireLinks(称之为class A),如名称所示,使用link_queue中的项目,并将每个链接的任意数量(> = 1)的页面放入page_queue

相反,主线程class ProduceLinks_RequirePages(称之为class B)会使用page_queue中的网页,并将任意数量(> = 0)的链接排入link_queue

现在,class B生成的链接可能比class A生成网页的速度快。 另一方面,反过来也是可能的。

如何在Ruby 1.9.2中正确同步这些线程?

我试图在两者中使用显示器,但在某些时候我最终会遇到死锁。

(如果我没有准确,请通过评论告诉我并发布一些示例课程)


修改 正在发生的事情的图片:

enter image description here

实施例

link_queue初始化为1个项目 page_queue初始化为0项

我们有4个class A主题和1个class B主题。每行将是1个步骤。


线程A.1抓取1个链接(linkQ = 0)输出1页(pageQ = 1)

线程B抓取1页(pageQ = 0)输出400个链接(linkQ = 400)

线程A.3抓取1个链接(linkQ = 399)输出1页(pageQ = 1)

线程A.2抓取1个链接(linkQ = 398)输出1页(pageQ = 2)

线程B抓取1页(pageQ = 1)输出100个链接(linkQ = 498)

线程A.1抓取1个链接(linkQ = 497)输出1页(pageQ = 2)

线程A.4抓取1个链接(linkQ = 496)输出1页(pageQ = 3)

线程B注意到linkQ太大并等到linkQ <16

。 。 。线程A. *继续工作。 。 。之后(linkQ = 15)和(pageQ = 484)

现在我们遇到了相反的问题。 现在线程A必须等到pageQ降到某个阈值以下。 否则我们会在某些时候耗尽内存。

欢呼声

2 个答案:

答案 0 :(得分:2)

无论您使用的是Ruby还是其他任何语言,无论何时您都有像这样描述的生产者 - 消费者设计,无论生产者和消费者是线程还是流程,您都必须永远假设消费者能够“跟上”生产者。您必须始终使用有界队列。即使使用你在评论中提到的外部队列也不能解决一般情况下的问题,因为虽然比RAM大得多,但外部存储并不是无限的。

Ruby标准库有SizedQueue,您可以使用require 'thread'获得。 SizedQueue是一个线程安全的队列,其大小有限。如果生产者线程在项目已满时尝试将项目推送到队列,则生产者将阻塞,直到消费者从队列中弹出一个项目(为新项目腾出空间)。这将使消费者有机会“赶上”。同样,如果消费者线程在空的时候尝试从队列中弹出一个项目,那么消费者将阻止该项目可用。

如果整体吞吐量受到生产者的限制,他们往往会获得更多的CPU时间(因为消费者阻止)。另一方面,如果消费者是瓶颈,他们往往会获得更多的CPU时间。这比允许生产者使用不断增长的项目来消耗填充队列的系统资源更好,而消费者可能正在使用这些资源来处理积压工作。

答案 1 :(得分:0)

根据您的说法,您似乎有反馈循环问题。所以在我开始回答问题的同步部分之前,我不得不问你问题的范围是什么?

如果您正在构建一个Web爬虫系统,您所描述的将尝试枚举Internet上的每个页面,并且没有任何线程同步可以帮助您将其放入RAM中。

循环怎么样?页面链接到b和c,页面b链接到页面a和c等?您描述问题的方式,每次迭代将以指数方式增长。如果您要处理的页数有限,它有多大?如果你反复遍历一些页面,你是否应该在最近处理的基础上跳过页面?

总而言之,为了解决这个问题,你必须确保平均一个周期产生另一个新周期,通过一些产生零页而不是一个的链接,以及产生0个链接的页面。

或者,你想做什么?其他方法可能更合适。