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

Lum*_*umi 30 parallel-processing perl multithreading

如果您想了解如何使用Perl解释器线程,有良好的文档perlthrtut(线程教程),并threads附注手册页.写一些简单的脚本绝对够好.

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

这些线程,可当perl -V:useithreadsuseithreads='define';和释放use threads,也被称为的ithread,也许更恰当,以便他们有很大的不同,从由Linux或Windows操作系统或Java虚拟机中的任何内容所提供的线程默认情况下,而是一个共享大量数据被复制,而不仅仅是线程堆栈,因此显着增加了进程大小.(要查看效果,请在测试脚本中加载一些模块,然后在循环中创建线程,每次都按下按键,并观察任务管理器中的内存增加或top.)

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

- 编程Perl ithreads之前需要了解的事情(Perlmonks 2003)

在研究Perl ithreads的主题时,你会看到人们不鼓励你使用它们("非常糟糕的主意","从根本上有缺陷",或"从不使用ithreads for any").

Perl线程教程强调"Perl线程是不同的",但它没有太多麻烦解释它们是如何不同以及这对用户意味着什么.

有关ithreads实际内容的有用但非常简短的解释来自WINDOWS PROCESS EMULATIONCoro标题下的联机帮助页.该模块的作者(Coro - perl中唯一真正的线程)也不鼓励使用Perl解释器线程.

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

有从2003年Perlmonks页面(你需要的东西之前的Perl编程的ithread知道),其中笔者问:"现在你可能想知道为什么Perl中的ithread不使用fork()那不是已经取得了很多更有意义?" 这似乎是由forkspragma 的作者写的.不确定该页面上提供的信息在2012年仍适用于较新的Perls.

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

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

Dav*_*esh 22

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

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

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

在"工人"ithreads:

  1. 带入任何通用代码(适用于任何类型的工作)
  2. 阻止 - 从队列中取出一项工作
  3. 需求加载此工作所需的任何其他依赖项.
  4. 做的工作.
  5. 通过"result"队列将结果传递回主线程.
  6. 回到2.

如果某些"工作者"线程开始变得有点强劲,并且您需要将"工作"线程限制为某个数字,然后在其位置启动新线程,然后首先创建"启动器"线程,其工作是启动"工作者" "线程并将它们连接到主线程.

Perl ithreads有哪些主要问题?

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

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

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

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

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

结果:

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

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

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

有一天,在Perl 6中可能会有更好的表现.

  • 大多数情况下,它是关于大多数实现中的Python GIL(主要是cpython,但其他更小的范围).基本上,这意味着Python上的OS线程实际上不能同时执行CPU工作 - 这完全违背了它们的要点.您可以使用它们(Python线程)使I/O更加并发,但使用面向事件的API更容易实现. (3认同)

小智 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,如果我做了很多小的或简短的事情,那么一些大型或长期存在的东西的线程.