Perl:在线程之间共享复杂数据结构

4
我想分享一个复杂的数据结构给多个线程。据我所知,使用threads:shared不可能达到目的(只有基本类型可以共享)。
因此,我考虑用JSON或Storable对该结构进行序列化/反序列化,这样它就成为了一个可以共享的字符串。但是,在使用前需要将其拆包,在更改后需重新打包。
  • 这是解决问题的常见方法吗?

  • 是否有更好的方法?

  • 您更喜欢使用JSON、Storable还是其他什么技术?

谢谢帮助!
编辑
我刚刚测试了一下Storable和JSON。JSON更快,并且产生更小的序列化字符串。我没有预料到这一点。
2个回答

3
处理这个问题时,我使用Thread::Queue传递我的对象,并通常使用Storable进行序列化。我没有费力进行性能比较,因为通常情况下,数据传递的开销不是限制因素。Storable的关键优势在于它允许一些有限的对象支持(注意 - 要小心 - 它仅在你的对象是自包含的情况下才有效)。
#!/usr/bin/env perl
use strict;
use warnings;

package MyObject;

sub new { 
   my ( $class, $id ) = @_; 
   my $self = {};
   $self -> {id} = $id; 
   $self -> {access_count} = 0; 
   bless $self, $class;
   return $self;
}

sub access_thing { 
   my ( $self ) = @_;
   return $self -> {access_count}++; 
}

sub get_id { 
    my ( $self ) = @_;
   return $self -> {id}; 
}

package main; 

use threads;
use Thread::Queue;

use Storable qw ( freeze thaw );

my $thread_count = 10;

my $work_q = Thread::Queue -> new; 

sub worker  {
   while ( my $item = $work_q -> dequeue ) {
      my $obj = thaw ( $item ); 
      print $obj -> get_id, ": ", $obj -> access_thing,"\n";    

   }
}

for (1..$thread_count) {
   threads -> create (\&worker); 
}

for my $id ( 0..1000 ) {
   my $obj = MyObject -> new ( $id ); 
   $work_q -> enqueue ( freeze ( $obj ) );
}

$work_q -> end;

$_ -> join for threads -> list; 

如果JSON只能限制你使用数组/哈希数据结构——这对于您的用例可能是可以接受的。

2

使用 shared_clone 可以共享复杂的数据结构。在将数据结构的组件添加到中之前,需要对其进行克隆。

use strict;
use feature 'say';
use Data::Dump qw(dd);

use threads;
use threads::shared;

my $cds = {
    k1 => shared_clone( { k1_l2 => [ 1..2 ] } ),
    k2 => shared_clone( { k2_l2 => [10..11] } )
};

my @threads = map { async(\&proc_ds, $cds->{$_}) } keys %$cds;

$_->join() for @threads;

dd $cds;
    
sub proc_ds {
    my ($ds) = @_;
    lock $ds;
    push @{$ds->{$_}}, 10+threads->tid  for keys %$ds;
}

请注意,在处理共享值时,不要允许自动创建未共享(且为空)的组件。需要明确检查其是否存在。
准备好的数据结构需要进行克隆和共享。
my $cds = { k => [ 5..7 ] };          # already built, need be shared
my $cds_share = shared_clone( $cds );

my @threads = map { async(\&proc_ds, $cds_share) } 1..3;
$_->join() for @threads;

使用与上面相同的proc_ds(),这将打印结构体(缩略输出):
{ 'k' => [ '5', '6', '7', '11', '12', '13' ] };
当数据结构被填充以进行共享时,就像第一个示例一样,那么需要支付的开销就会更少。否则就会涉及到数据复制,就像第二个示例中一样,是否可以接受取决于细节(数据大小、复制频率等)。
序列化数据的想法也是可行的,但它的适用性再次取决于细节,因为在这种情况下,你不仅要复制数据,还要访问磁盘。
在这种情况下,JSON肯定是一个很好的选择,因为它是一个简单易读的数据格式,也可以在工具之间共享。Storable是二进制的,直接处理Perl数据结构,并且应该很快(在处理大型数据时应该表现出来)。
另一个选项是使用worker模型并通过消息队列传递数据。然后你可以使用Thread::Queue,或者可能利用Thread::Queue::Any进行通信。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接