3

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

同步实例方法:

类 class_name { 同步类型 method_name() { \statement block } }

方法中的所有语句都变成了同步块,实例对象就是锁。这意味着如果我告诉一个线程使用这个方法,它会等到前一个线程使用完这个方法。有没有办法在 Perl 中做到这一点?

4

3 回答 3

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上面的代码来完成的——你可以使用对象本身作为锁。

use threads::shared qw( shared_clone );

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

输入方法时将其锁定。

sub method {
   my ($self) = @_;
   lock %$self;
   ...
}
于 2012-05-09T15:50:24.070 回答
2

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

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

于 2012-05-09T15:06:57.637 回答
0

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

这本质上是@ikegami 的简化解决方案,隐藏在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!
于 2013-08-12T15:10:06.717 回答