【问题标题】:Perl - How to share objects between threadsPerl - 如何在线程之间共享对象
【发布时间】:2017-11-09 09:50:22
【问题描述】:

我的脚本有对象的散列 - 所有单个类 (SampleClass.pm)。以下是我用来创建对象的方式。

$objectHash{'foo'} = SampleClass->new();
$objectHash{'bar'} = SampleClass->new();
.
.

然后我生成了几个线程(比如 5 个),每个线程都按照指示进行工作。

现在说线程 1 写入一个对象 -

$objectHash{'foo'}->settimeWhenISaidHello($time);

然后退出。现在,当 Thread2 开始工作并检查如下值时

$lastHelloTime = $objectHash{'foo'}->gettimeWhenISaidHello($time);

它得到未定义或空值。我想跨线程共享这些值。怎么可能?

另外,我的类构造函数new 具有哈希和哈希数组作为成员变量,如下所示。

sub new
{
.
.
listOfGuysToSayHello = {}; #This is an array with guy name as key and array value as data
SchoolsWithStudentsToSayHellow = {}; #this is array of hashes
.
.
}

此外,我已经解决了这个问题 - how to access perl objects in threads 并且答案没有满足我的要求。

请告诉我你的想法。

【问题讨论】:

标签: multithreading perl oop object


【解决方案1】:

我建议首先共享对象不是一个好主意 - 可以使用 threads::shared 完成,但有一些限制,并且您可能会引入一些竞争条件。

因此,我会说 - 使用Thread::Queue 在线程之间进行通信,并使用Storablefreezethaw 进行序列化。

这是一个有点简单的例子,但希望能说明这一点吗?也就是说,您不是在“共享”对象,而只是将它们作为序列化的数据结构传递。如果实例化一个对象确实发生了系统状态更改(例如打开与数据库的连接或类似情况),它确实会有些崩溃——但这在线程上下文中本质上是很难做到的。

#!/usr/bin/env perl

use strict;
use warnings;

package MyObject;

sub new {
   my ( $class, %args ) = @_;
   my $self = \%args;
   bless $self, $class;
   return $self;
}

sub get_value {
   my ( $self, $key ) = @_;
   return $self->{$key} // 0;
}

sub set_value {
   my ( $self, $key, $value ) = @_;
   $self->{$key} = $value;
}

package main;

use threads;
use Storable qw ( freeze thaw );
use Thread::Queue;

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

sub worker {
   while ( my $serialised = $work_q->dequeue ) {
      my $local_obj = thaw $serialised;
      print threads->self->tid, " is processing object with id ",
        $local_obj->get_value('id'), "\n";
      $local_obj->set_value( 'processed_by', threads->self->tid );
      $result_q->enqueue( freeze $local_obj );
   }
}

threads->create( \&worker ) for 1 .. 10;

for ( 1 .. 100 ) {
   my $obj = MyObject->new( id => $_ );
   $work_q->enqueue( freeze $obj );
}
$work_q->end;
$_->join for threads->list;

while ( my $ser_obj = $result_q->dequeue_nb ) {
   my $result_obj = thaw $ser_obj;
   print "Object with ID of :", $result_obj->get_value('id'),
     " was processed by thread ", $result_obj->get_value('processed_by'),
     "\n";
}

【讨论】:

  • 我将查看Storable 库并返回这里!感谢 Sobrique 的回答!
【解决方案2】:

发生这种情况是因为 Perl 本身并不在线程之间共享对象——或者任何东西,实际上。当 Thread2 启动时,它会获取当时程序状态的副本,并从那里开始。因此,如果 Thread1 在 Thread2 创建后更改了任何值,则此更改将仅对 Thread1 可见。

现在,有几种方法可以解决这个问题。正如@Borodin 提到的,您可以使用外部库为您处理数据交换。如果您不想依赖外部库,或者如果您由于某种原因无法安装它,您可以通过在它们之间使用某些东西来共享这些数据,例如临时系统文件或数据库。它并不像听起来那么复杂,我们已经在我的公司实现了它,并且效果很好。

编辑:正如下面@PerlDuck 巧妙地指出的那样,threads::shared 已经成为 Perl 的核心模块已经有一段时间了......

【讨论】:

  • threads::shared 是自 Perl 5.7.3 以来的核心模块。它不能分享“太复杂”的东西,但文档非常清楚什么是可能的,什么不是。
  • ...现在我不知道!我想这就是当您的公司在十多年来一直使用相同的 Perl 版本时会发生这种情况......我们刚刚更新了我们的系统以使用 RHEL 7,事实上threads::shared 是默认安装的。感谢您指出这一点。
  • 如果你想知道我是怎么知道这么精确的:脚本 corelist 与 Perl 一起出现。它告诉哪个模块是/曾经是核心模块,因为/直到什么时候等等。试试corelist threads::sharedcorelist --man。 :-)
  • @BalaKrishnan 一个技巧是将您的数据结构序列化为字符串(例如 JSON、Data::Dumper),共享该字符串,并在访问时对其进行反序列化。缓慢而繁琐,但会奏效。
  • 不要忘记 Storable 作为序列化选项 - freeze thawThread::Queue 配合得非常好。 - 它实际上可以很好地处理对象,尽管它不像 JSON 那样可移植。 (例如,不是二进制可移植的,并且可能跨平台有问题)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-01
  • 2011-09-26
相关资源
最近更新 更多