Perl中的ithreads(解释器线程)的用例以及使用或不使用它们的基本原理?

时间:2012-04-02 09:24:24

标签: multithreading perl parallel-processing

如果您想学习 如何使用Perl解释器线程,perlthrtut (threads tutorial)the threads pragma manpage中有很好的文档。写一些简单的脚本肯定是足够好的。

但是,我在网上找不到关于为什么 明智地使用Perl的解释器线程的指导。事实上,关于它们的讨论并不多,如果人们谈论它们,通常会阻止人们使用它们。

perl -V:useithreadsuseithreads='define';并由use threads释放时可用的这些主题也称为 ithreads ,也可能更合适,因为它们非常不同来自Linux或Windows操作系统或Java VM提供的线程,默认情况下不共享任何内容,而是复制大量数据,而不仅仅是线程堆栈,从而显着增加了进程大小。 (要查看效果,请在测试脚本中加载一些模块,然后在循环中创建线程,每次都按下按键,并在任务管理器或top中观察内存上升。)

  

[...]每次启动一个线程时,所有数据结构都被复制到   新线程。当我说全部,我的意思是全部。这例如包括   包存明,全局变量,范围内的词汇。所有的一切!

- Things you need to know before programming Perl ithreads (Perlmonks 2003)

在研究Perl ithreads的主题时,您会看到人们不鼓励您使用它们("extremely bad idea", "fundamentally flawed""never use ithreads for anything")。

The Perl thread tutorial highlights that "Perl Threads Are Different",但没有太多麻烦来解释它们的不同之处以及这对用户意味着什么。

有关ithreads实际内容的有用但非常简短的解释是from the Coro manpage under the heading WINDOWS PROCESS EMULATION。该模块的作者( Coro - perl中唯一真正的线程)也不鼓励使用Perl解释器线程。

在某处,我读到在启用线程的情况下编译perl将导致解释器显着变慢。

2003年有一个Perlmonks页面(Things you need to know before programming Perl ithreads),其中作者问道:“现在你可能想知道为什么Perl ithreads没有使用fork()?这不是更有意义吗? “这似乎是forks pragma的作者写的。不确定该页面上提供的信息在2012年仍然适用于较新的Perls。

以下是Perl中线程使用的一些指导原则我从读数中提炼出来(可能是错误的):

到目前为止我的研究。现在,感谢您在Perl中解决这个线程问题的任何亮点。 Perl中的ithreads有哪些明智的用例?使用或不使用它们的理由是什么?

2 个答案:

答案 0 :(得分:22)

简短的回答是它们很重(你不能廉价地推出其中的100多个),并且它们表现出意想不到的行为(最近的CPAN模块有所缓解)。

可以安全地使用Perl ithreads将它们视为独立的Actors

  1. 为“工作”创建一个Thread :: Queue :: Any。
  2. 启动多个ithreads和“result”队列传递它们(“工作”+自己的“结果”)队列关闭。
  3. 加载(要求)应用程序所需的所有剩余代码(而不是在线程之前!)
  4. 根据需要将线程的工作添加到队列中。
  5. 在“工人”ithreads:

    1. 带入任何通用代码(适用于任何类型的工作)
    2. 阻止 - 从队列中取出一项工作
    3. 需求加载此工作所需的任何其他依赖项。
    4. 做好工作。
    5. 通过“result”队列将结果传回主线程。
    6. 回到2。
    7. 如果某些“工作”线程开始变得有点强大,并且你需要将“工作”线程限制为某个数字,然后在它们的位置启动新线程,然后首先创建一个“启动器”线程,它的工作是启动“worker”线程并将它们连接到主线程。

      Perl ithreads有哪些主要问题?

      对于“共享”数据,它们有点不方便,因为您需要明确地进行共享(不是大问题)。

      你需要注意使用DESTROY方法的对象的行为,因为它们超出了某个线程的范围(如果它们在另一个线程中仍然需要它们!)

      重要的:未明确共享的数据/变量 CLONED 到新线程中。这是一个性能打击,可能根本没有你想要的。解决方法是从一个非常“原始”的条件启动ithreads(加载的模块不多)。

      IIRC,Threads ::命名空间中有一些模块可以帮助明确依赖关系和/或清理新线程的克隆数据。

      另外,IIRC,使用称为“公寓”线程的ithreads有一个略有不同的模型,由Thread :: Appartment实现,它具有不同的使用模式和另一组权衡。

      结果:

      除非你知道自己在做什么,否则不要使用它们: - )

      Fork 可能在Unix上更有效率,但IPC的故事对于ithreads来说更简单更简单。 (自从我上次查看以来,CPAN模块可能已经减轻了这种情况: - )

      他们仍然比Python的线程更好。

      有一天,在Perl 6中可能会有更好的东西。

答案 1 :(得分:8)

我曾多次使用perl的“线程”。它们对于启动某个流程并继续使用其他流程非常有用。我对他们如何在幕后工作的理论方面没有太多的经验,但我确实有很多实际的编码经验。

例如,我有一个服务器线程,它监听传入的网络连接,并在有人要求时发出状态响应。我创建该线程,然后继续创建另一个监视系统的线程,检查五个项目,睡眠几秒钟,然后再次循环。收集监视器数据可能需要3-4秒,然后将其推送到共享变量中,服务器线程可以在需要时读取该数据并立即将最后已知结果返回给任何人询问。监视器线程在发现项目处于错误状态时,启动一个单独的线程来修复该项目。然后它继续前进,检查其他项目,同时修复坏项目,并开始其他线程以获取其他不良项目或加入完成的修复线程。主程序始终每隔几秒循环一次,确保监视器和服务器线程不可连接/仍在运行。所有这些都可以用一些独立的程序编写,利用其他形式的IPC,但perl的线程使它变得简单。

我使用它们的另一个地方是分形发生器。我会使用一些算法分割图像的一部分,然后启动尽可能多的线程,因为我有CPU来完成工作。他们将每个结果填充到一个GD对象中,这不会导致问题,因为他们每个都在数组的不同部分工作,然后在完成后我会写出GD图像。这是我使用perl线程的介绍,并且是一个很好的介绍,但后来我用C重写了它,它快了两个数量级:-)。然后我重写了我的perl线程版本以使用Inline :: C,它只比纯C版本慢20%。尽管如此,在大多数情况下,由于CPU密集型而你想要使用线程,你可能只想选择另一种语言。

正如其他人所提到的,fork和thread真的重叠了很多用途。但是,Coro并不真正允许多CPU使用或者像fork和thread那样的并行处理,你只会看到你的进程使用100%。我过度简化了这一点,但我认为描述Coro最简单的方法是它是你的子程序的调度程序。如果您有一个阻止的子例程,您可以跳到另一个子例程并在等待时执行其他操作,例如,您有一个计算结果并将其写入文件的应用程序。一个块可能会计算结果并将其推入通道。当它用完时,另一个块开始将它们写入磁盘。当该块在磁盘上等待时,另一个块可以在更多工作时再次开始计算结果。不可否认,我对Coro做的并不多;这听起来像是一种加速某些事情的好方法,但是由于不能同时做两件事我有点迟钝。

如果我想进行多处理,我自己的个人偏好就是使用fork,如果我做了很多小事或短事,那就是一些大型或长寿命的线程。