假设我创建了三个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!
有没有确保它们以正确的顺序输出而不将它们全部压缩成一行?
答案 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
删除它,而不是让它超出范围)。
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
(您也可以像第一个示例中那样锁定队列)。这又一次,对你的例子来说可能有些过分,但如果你试图将结果整理成特定的顺序,它会很有用。