【问题标题】:How do I make a synchronized method in perl?如何在 perl 中创建同步方法?
【发布时间】:2012-05-09 15:02:38
【问题描述】:

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

同步实例方法:

类类名{ 同步类型 method_name() { \语句块 } }

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

【问题讨论】:

    标签: multithreading perl thread-safety


    【解决方案1】:

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

    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;
       ...
    }
    

    【讨论】:

    • 已经很简单了,我只是添加了如何让它变得更简单。
    • 你实际上需要在上面的代码中指定threads::shared::shared_clone,否则你会得到一个错误,因为shared_clone不在包中。您的示例忽略了指定一个包来定义一个类,因此您的代码有效,因为它实际上不是面向对象的。
    • @CDahn,不,如果您使用 use threads::shared qw( shared_clone ); 导入它,则不会。我已经添加了这个。 /// 你误会了;我确实定义了一个类,并且正在创建该类的对象。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      旧的 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!
      

      【讨论】:

        猜你喜欢
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        • 2013-07-12
        相关资源
        最近更新 更多