在 Java 中,当我创建线程并共享对象时,有时我希望线程访问相同的对象方法,但我不希望它们同时执行此操作。因此,为避免这种情况,我将对象方法定义为 Synchronized 方法,如下所示。
同步实例方法:
类 class_name { 同步类型 method_name() { \statement block } }
方法中的所有语句都变成了同步块,实例对象就是锁。这意味着如果我告诉一个线程使用这个方法,它会等到前一个线程使用完这个方法。有没有办法在 Perl 中做到这一点?
在 Java 中,当我创建线程并共享对象时,有时我希望线程访问相同的对象方法,但我不希望它们同时执行此操作。因此,为避免这种情况,我将对象方法定义为 Synchronized 方法,如下所示。
同步实例方法:
类 class_name { 同步类型 method_name() { \statement block } }
方法中的所有语句都变成了同步块,实例对象就是锁。这意味着如果我告诉一个线程使用这个方法,它会等到前一个线程使用完这个方法。有没有办法在 Perl 中做到这一点?
在构造函数中创建一个互斥锁。
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;
...
}
旧的 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!