【问题标题】:How To Prevent Perl Thread Printouts from Intercepting?如何防止 Perl 线程打印输出被拦截?
【发布时间】:2015-12-31 19:29:58
【问题描述】:

假设我创建了三个 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!

是否有任何方法可以确保它们以正确的顺序输出而不将其全部压缩成一行?

【问题讨论】:

  • 1.你能显示你的代码吗? 2、如果要顺序输出,真的需要线程吗?这里线程的用例是什么?

标签: multithreading perl unix printf


【解决方案1】:

首先:don't use perl interpreter threads

话虽如此,为了防止这些行被单独打印,您的选择是:

  • 在打印这两行时获取一个信号量,以防止两个线程同时进入该临界区。
  • 打印带有嵌入换行符的单行文本,例如:

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

【讨论】:

【解决方案2】:

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

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

这样做的一个缺点是——正如你所发现的——你不能保证操作的顺序或原子性。复合操作也是如此——你实际上不能保证即使 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,但与 lock 相比,您不会获得太多。 (只是up() 删除它的能力,而不是让它超出范围)。

使用带有Thread::Queue 的“IO 处理程序”线程

(在分叉中,您可以在此处使用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 操作,它们可能会再次交错,原因完全相同。

所以你需要;

$output -> enqueue ( "Print this message\n", "And this message too\n" ); 

(您也可以像第一个示例中那样锁定队列)。再说一遍,对于您的示例来说可能有点矫枉过正,但如果您尝试将结果整理到特定的顺序中,它可能会很有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    相关资源
    最近更新 更多