2013-02-14 41 views
7

Perl tân binh ở đây, vì vậy hãy nhẹ nhàng :)Perl luồng của phương pháp đối tượng

Tôi đã viết mã sau để theo dõi con chó của tôi khi tôi đang săn (không thực sự). Mỗi khi một con chó tìm thấy một con vịt, nó báo hiệu sợi chính, sau đó thu thập thông tin từ mỗi con chó trong gói.

#!/usr/bin/env perl 

use strict; 
use warnings; 
use v5.14; 

use threads; 

{ 
    package Dog; 

    sub new { 
     my ($class, $name, $dt) = @_; 
     my $self = { 
      dt => $dt,  # will find a duck every $dt seconds 
      name => $name, 
      ducksfound => 0 
     }; 
     bless $self, $class; 
    } 

    sub hunt { 
     # 
     # the "thread" method -- the dog will hang around for $dt seconds, 
     # then alert the main thread by sending SIGUSR1 
     # 
     my $self = shift; 
     while (1) { 
      sleep $self->{dt}; 
      $self->{ducksfound} += 1; 
      kill USR1 => $$; 
     } 
    } 

    sub bark { 
     my $self = shift; 
     sprintf "%s: found %d ducks!", ($self->{name}, $self->{ducksfound}); 
    } 

    1; 
} 

my @dogs; 

$SIG{USR1} = sub { 
    say join ", ", map { $_->bark } @dogs; 
}; 


push @dogs, Dog->new("Labrador", 1); 
push @dogs, Dog->new("Retriever", 2); 
push @dogs, Dog->new("Shepherd", 3); 

threads->create(sub { $_->hunt }) for @dogs; 
$_->join for threads->list; 

sản lượng dự kiến ​​của các mã trên sẽ là một cái gì đó như:

Labrador: tìm thấy 1 con vịt !, Retriever: tìm thấy 0 vịt !, Shepherd: tìm thấy 0 vịt!

Labrador: tìm thấy 2 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 3 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 3 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 4 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 5 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 6 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 6 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 6 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 1 con vịt!

Thay vào đó, những gì tôi nhận được là như sau:

Labrador: tìm thấy 1 con vịt !, Retriever: tìm thấy 0 vịt !, Shepherd: tìm thấy 0 vịt!

Labrador: tìm thấy 2 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 3 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 0 con vịt !, Retriever: tìm thấy 1 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 4 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 5 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 0 con vịt !, Retriever: tìm thấy 2 con vịt !, Shepherd: tìm thấy 0 con vịt!

Labrador: tìm thấy 0 con vịt !, Retriever: tìm thấy 0 con vịt !, Shepherd: tìm thấy 1 con vịt!

Lưu ý cách đếm của mỗi con chó được đặt lại về 0 tiếng mà một chú chó khác đang nói.

Bất kỳ thông tin chi tiết nào về chú thích cụ thể mà tôi phải chú ý khi đọc Llama?

+2

Đây là một câu hỏi khá hay cho một tân binh Perl. :) –

+1

tín hiệu và chủ đề không pha trộn tốt. bạn không thể báo hiệu một chuỗi cụ thể afaik. cập nhật: có vẻ như các chủ đề doc không đồng ý, nhưng hiển thị bằng cách sử dụng '$ thr-> kill', không đồng nghĩa giết – ysth

+0

@JonahBishop - cảm ơn, tôi đoán :) Got mất kiên nhẫn nửa chừng qua Alpaca, bắt đầu gãi một trong những itches proverbial của tôi ... đoán đây là những gì tôi nhận được cho mã hóa lần lượt :) –

Trả lời

7

Vấn đề cơ bản là các biến Perl không được chia sẻ theo mặc định, kết hợp với một chút kỳ quặc về chuỗi nào đang phục vụ tín hiệu để tạo ra kết quả bạn đang xem.

Khi bạn sinh ra các chủ đề săn bắn, mỗi chủ đề sẽ có một bản sao riêng của @dogs và nội dung của nó. Đó chỉ là cách các chủ đề Perl hoạt động: trình thông dịch và trạng thái hiện tại của nó - @dogs, %SIG, mở STDOUT - được sao chép toàn bộ. Để xem cách làm việc, xem xét mã này:

my %dog_decls = (
    Labrador => 1, 
    Retriever => 2, 
    Shepherd => 3, 
); 

while (my ($name, $delay) = each %dog_decls) { 
    my $dog = Dog->new($name, $delay); 
    push @dogs, $dog; 
    threads->create(sub { $dog->hunt }); 
} 

$_->join for threads->list; 

Các nhân bản xảy ra tại threads->create thời gian, vì vậy mỗi một trong các chủ đề là nhận được một phiên bản khác của @dogs mang theo nó. Kết quả là, danh sách các Dogs mà sủa khi một trong số họ bắt một con vịt phụ thuộc vào chủ đề bắt tín hiệu! (Cũng lưu ý rằng bạn có thể suy ra thứ tự mà trong đó each xảy ra để phát ra băm từ đầu ra này.)

Retriever: found 0 vịt !, Labrador: tìm thấy 1 con vịt!

Retriever: tìm thấy 0 con vịt !, Labrador: tìm thấy 2 con vịt!

Retriever: tìm thấy 1 con vịt!

Retriever: tìm thấy 0 con vịt !, Labrador: tìm thấy 3 con vịt!

Retriever: tìm thấy 0 con vịt !, Labrador: tìm thấy 4 con vịt!

Retriever: tìm thấy 0 con vịt !, Labrador: tìm thấy 0 con vịt !, Shepherd: tìm thấy 1 con vịt!

Về mã của bạn: Khi Labrador chủ đề (thread 1) tỉnh dậy, nó cập nhật Labrador 's ducksfound và gửi một SIGUSR1. Một người nào đó (và chúng tôi sẽ nói thêm về ai trong một giây) xem tín hiệu và barks tất cả các Dogs. Nhưng chỉ có Labrador đó là thay đổi là một trong thread 1. Các chủ đề RetrieverShepherd (chủ đề 2 và 3 tương ứng) đã không nhìn thấy bản cập nhật cho ducksfound của .

Tại sao giá trị cho ducksfound được in chính xác lúc đầu? Vì cách bạn cài đặt trình xử lý tín hiệu. Bạn đã cài đặt nó trên toàn bộ quy trình - gọi lại rằng tôi đã nói %SIG là một trong số những thứ được nhân bản với chủ đề của bạn. Vì vậy, mỗi chủ đề có trình xử lý cho USR1 gây ra tất cả các số Dogs đến bark. Khi bạn gửi USR1 đến $$, bất kỳ chủ đề nào xảy ra khi tỉnh táo tại thời điểm đó sẽ bắt được nó. Và nó như vậy xảy ra rằng các chủ đề mà gửi tín hiệu là thread đó là tỉnh táo.

Và điều đó giải thích lý do tại sao khi số Retriever bắt con vịt đầu tiên, giá trị ducksfound của nó là chính xác nhưng không phải là Labrador. Retriever bắt vịt trong chủ đề 2, gửi SIGUSR1 đến chính nó và sau đó barks tất cả các số Dogs. Nhưng trong chuỗi 2, số Labrador chưa bao giờ được cập nhật và vì vậy vỏ cây hiển thị 0 cho Labrador và 1 cho Retriever.

Vấn đề của các biến không chia sẻ có thể được nhận xung quanh khá đơn giản bằng cách sử dụng các threads::shared:

use threads::shared; 
... 
my @dogs :shared; 
... 
push @dogs, shared_clone(Dog->new("Labrador", 1)); 

Bây giờ khi một thread cập nhật một Dog, tất cả các chủ đề sẽ thấy nó và vì vậy nó không quan trọng mà chủ đề là phục vụ tín hiệu. Đó là tốt, bởi vì trong mã của bạn "chủ đề chính" (thread 0) không bao giờ được kiểm soát trở lại. Điều này có thể được, nhưng có lẽ dẫn đến hành vi hơi weirder hơn bạn mong đợi.

Nếu bạn thực sự muốn có tồn tại một sợi quản lý, bạn có thể cần phải đẻ trứng nó một cách rõ ràng:

# in Dog::new 
     my ($class, $name, $hunter, $dt) = @_; 
     ... 
     hunter => $hunter, 
# in Dog::hunt 
     $self->{hunter}->kill('USR1'); 
# in main 
my $hunter_thread = threads->create(
    sub { 
     local $SIG{USR1} = sub { 
      say join ", ", map { $_->bark } @dogs; 
     }; 
     while (1) { usleep 100_000 } # higher resolution than hunt events 
    } 
); 
... 
push @dogs, shared_clone(Dog->new("Labrador", $hunter_thread, 1)); 

Lưu ý rằng chỉ cần đặt trong một thread quản lý mà không chia sẻ Dogs của bạn sẽ cho kết quả trong một thread mà tỉnh dậy để in một loạt các số 0. Bạn cần phải làm cả hai để có được kết quả mà bạn mong đợi.

+0

> Khi bạn sinh ra các chủ đề săn bắn, mỗi chủ đề sẽ có bản sao @dogs và nội dung của nó. Những gì tôi dự định là cho 'push @dogs, Dog-> new (" Labrador ", 1); ' để sinh ra một thể hiện của một số' Dog' và lưu một tham chiếu trong hunter (thread zero). Làm thế nào mà 'Dog' biết về' chó '? Thông thường tôi sẽ chỉ ngồi xuống và làm như tôi đã nói, nhưng nhìn thấy như tôi đang cố gắng để tìm hiểu công cụ này, tôi tìm thấy nó alright là một chút dày đặc ... :) –

+1

Cách chủ đề Perl làm việc, các thông dịch viên toàn bộ được nhân bản cùng với trạng thái hiện tại của nó. Bạn thực sự tạo '@ dogs' trong chủ đề 0. Sau đó, các lời gọi' threads-> create' sao chép tất cả trạng thái hiện tại - bao gồm '@ dogs' và'% SIG' - thành chuỗi mới. Tôi tin rằng đây là những bản sao COW và do đó không quá nặng nề, nhưng hành vi này là lý do tại sao hướng dẫn về chủ đề Perl khuyên bạn đẻ trứng trước khi tải quá nhiều mô-đun hoặc làm quá nhiều công việc trong chuỗi 0. – darch

+0

@ K-spacer Đã thêm ví dụ về điều đó câu trả lời. – darch

Các vấn đề liên quan