如何防止Perl线程打印输出被拦截?

时间:2015-12-31 01:16:00

标签: multithreading perl unix printf

假设我创建了三个perl线程,每个线程执行此操作:

print hi I am thread $threadnum!;

print this is a test!;

你会期望输出: hi I am thread 1

this is a test!

hi I am thread 2

this is a test!

hi I am thread 3

this is a test!

但相反,大约一半时间发生的事情是: hi I am thread 1

this is a test!

hi I am thread2

hi I am thread3

this is a test!

this is a test!

有没有确保它们以正确的顺序输出而不将它们全部压缩成一行?

2 个答案:

答案 0 :(得分:1)

首先:don't use perl interpreter threads

话虽这么说,为了防止这些行被单独打印,你的选择是:

  • 在打印这两行时获取信号量,以防止两个线程同时进入该关键部分。
  • 使用嵌入的换行符打印单行文本,例如:

    print "hi I am thread $threadnum\nthis is a test!\n";
    

答案 1 :(得分:1)

从根本上说,你误解了线程的作用。它们被设计为并行和异步操作。

这意味着不同的线程在不同的时间点击程序的不同位,并且可能在不同的处理器上运行。

这样做的一个缺点是 - 正如您所发现的那样 - 您不能保证操作的顺序或原子性。复合操作也是如此 - 你实际上可以保证即使print语句也是原子操作 - 你最终可以得到分割线。

你应该总是假设任何操作都不是原子,除非你确定不知道,并相应地锁定。大多数时候你会逃脱它,但是你可以发现自己绊倒了一些真正可怕且难以发现的错误,因为在一小部分情况下,你的非原子操作会相互干扰。甚至像++这样的东西也可能没有。 (这不是你的线程本地变量的问题,只要你与共享资源交互,比如文件,STDOUT,共享变量等等。)

这是并行编程的常见问题,因此有许多解决方案:

使用lock和共享变量:

##outside threads:
use threads::shared; 
my $lock : shared;

在线程中:

{ 
   lock $lock;
   ### do atomic operation
}

当锁离开范围时,它将被释放。一个帖子将阻止'等待获得该锁,所以对于那一点,你不再是并行运行。

使用Thread::Semaphore

就像一个锁 - 你有Thread::Semaphore模块,在你的情况下......也是如此。但它围绕有限的(但超过1个)资源构建。我不会在您的方案中使用此功能,但如果您尝试使用此功能,则可能非常有用。限制并发磁盘IO和并发处理器使用 - 设置信号量:

use Thread::Semaphore;
my $limit = Thread::Semaphore -> new ( 8 );

在线程中:

$limit -> down(); 
 #do protected bit
$limit -> up(); 

您当然可以将8设置为1,但 <{1}}。 (只是能够lock删除它,而不是让它超出范围)。

使用&#39; IO处理程序&#39; up()

的主题

(在分叉时,你可以在这里使用Thread::Queue)。

pipe

在你的主题中,而不是use Thread::Queue; my $output = Thread::Queue -> new (); sub print_output_thread { while ( $output -> dequeue ) { print; } } threads -> create ( \&output_thread ); 你会使用:

print

此线程序列化您的输出,并确保每条消息都是原子的 - 但请注意,如果您执行两个 $output -> enqueue ( "Print this message \n" ); 操作,它们可能会再次交错,原因完全相同。

所以你需要;

enqueue

(您也可以像第一个示例中那样锁定队列)。这又一次,对你的例子来说可能有些过分,但如果你试图将结果整理成特定的顺序,它会很有用。