如何在perl中创建同步方法?

时间:2012-05-09 15:02:39

标签: multithreading perl thread-safety

在Java中,当我创建线程并共享一个对象时,我有时会希望线程访问相同的对象方法,但我不知道它们是什么时候同时执行它。因此,为了避免这种情况,我将对象方法定义为Synchronized方法,如下所示。

同步实例方法:

class class_name {    synchronized type method_name(){       \声明块    } }

方法中的所有语句都成为synchronized块,而实例对象是锁。这意味着如果我告诉一个线程使用这个方法,它将等到前一个线程完成使用该方法。 有没有办法在Perl中执行此操作?

3 个答案:

答案 0 :(得分:5)

在构造函数中创建一个互斥锁。

sub new {
   ...
   my $mutex :shared;
   $self->{mutex_ref} = \$mutex;
   ...
}

输入方法时锁定它。

sub method {
   my ($self) = @_;
   lock ${ $self->{mutex_ref} };
   ...
}

演示:

use strict;
use warnings;
use threads;
use threads::shared;
use feature qw( say );

sub new {
   my ($class, $id) = @_;
   my $mutex :shared;
   return bless({
      mutex_ref => \$mutex,
      id        => $id,
   }, $class);
}

sub method {
   my ($self) = @_;
   lock ${ $self->{mutex_ref} };
   say sprintf "%08X %s %s", threads->tid, $self->{id}, "start";
   sleep(2);
   say sprintf "%08X %s %s", threads->tid, $self->{id}, "end";
}

my $o1 = __PACKAGE__->new('o1');
my $o2 = __PACKAGE__->new('o2');

for (1..3) {
   async { my ($o) = @_; $o->method() } $o1;
   async { my ($o) = @_; $o->method() } $o2;
}

$_->join for threads->list();
  • 没有两个同时拨打$o1->method的电话。
  • $o1->method$o2->method的通话可以同时进行。

实际上,如果你打算共享对象 - 这是通过将对象作为参数传递给async int上面的代码完成的 - 你可以使用对象本身作为锁。

use threads::shared qw( shared_clone );

sub new {
   my ($class, ...) = @_;
   return shared_clone(bless({
      ...
   }, $class));
}

输入方法时锁定它。

sub method {
   my ($self) = @_;
   lock %$self;
   ...
}

答案 1 :(得分:2)

您可以使用semaphores在线程之间发出信号,或者在调用相关方法时使用lock共享对象,导致其他线程的任何后续调用都会阻塞,直到锁定线程完成该方法调用

在开始使用perl进行线程化之前,我强烈建议您阅读perl thread tutorial,因为perl的线程与其他语言不同

答案 2 :(得分:0)

旧的perl线程模型(来自5.005)支持属性:locked,或多或少可以满足您的需求。但是,对于当前的 ithreads 模型(5.8以后),您可以重新引入类似的属性。

这基本上是@ ikegami的simplified solution,隐藏在Attribute::Handlers的句法方便之后:

package Local::Sub::Attribute::Synchronized;
use strict;
use warnings;
use thread::shared;
use Attribute::Handler;

sub Synchronized : ATTR(CODE) {
  my (undef, $sym, $code) = @_;
  #
  # Lock the first argument (assumed to be a shared() object), then call $code
  # with the original @_
  #
  no warnings 'redefine';
  *{$sym} = sub { lock($_[0]); &$code; };
}

sub import {                              # Make :Synchronized available to our importer.
  my $callpkg = caller;                   # The usual technique is defines a UNIVERSAL::
  no strict 'refs';                       # handler, but I find that a bit ham-fisted.
  push @{"${callpkg}::ISA"}, __PACKAGE__;
}

允许你像这样编写你的课程:

package Foo;
use threads::shared;
use Local::Sub::Attribute::Synchronized;

sub new { shared_clone(...); }     # N.B.:  Your Foo object must be shared!

sub method_name : Synchronized {
  ...
}

你的代码是这样的:

$foo_object->method_name();  # Don't worry, it's synchronized!