2011-11-20 25 views
16

Chủ đề Perl không hỗ trợ chia sẻ tập tin. Tất cả các yếu tố của cấu trúc dữ liệu được chia sẻ phải được chia sẻ. Điều này cho thấy một vấn đề nếu một người cần chia sẻ một đối tượng có chứa một tập tin.Làm thế nào để chia sẻ một đối tượng có chứa một filehandle?

{ 
    package Foo; 
    use Mouse; 

    has fh => 
     is  => 'rw', 
     default => sub { \*STDOUT }; 
} 

use threads; 
use threads::shared; 
my $obj = Foo->new; 
$obj = shared_clone($obj);   # error: "Unsupported ref type: GLOB" 
print {$obj->fh} "Hello, world!\n"; 

Nó thực sự không quan trọng nếu tập tin tay "được chia sẻ" hay không, nó chỉ được sử dụng cho đầu ra. Có lẽ có một thủ thuật mà filehandle được lưu trữ bên ngoài đối tượng được chia sẻ?

Đối tượng này thực sự được chứa trong một đối tượng được chia sẻ khác trong một đối tượng khác, v.v. Sự trớ trêu lớn là các đối tượng được đề cập không bao giờ sử dụng chính các chủ đề, nhưng phải được phối hợp trong suốt quá trình nếu người dùng sử dụng các luồng.

Mã thực trong câu hỏi can be seen here: Các đối tượng này được sử dụng để định cấu hình nơi xuất có định dạng. Một đối tượng là cần thiết vì output does not always go to a filehandle.

+1

+1 để cuối cùng buộc tôi phải nghiên cứu chi tiết cách hoạt động của luồng trong Perl. -1 để ăn cắp 5 giờ ngủ :) – DVK

Trả lời

6

Tôi không có quyền truy cập vào chuỗi Perl lúc này, do đó, không thể đảm bảo rằng thao tác này sẽ hoạt động.

Tuy nhiên, một cách tiếp cận hơi đơn giản sẽ sử dụng một mức độ trừu tượng và lưu trữ một chìa khóa/index thành một filehandle băm/mảng toàn cầu vào đối tượng, một cái gì đó tương tự như sau:

my @filehandles =(); # Stores all the filehandles   ### CHANGED 

my $stdout; # Store the index into @filehandles, NOT filehandle. 
      # Should really be renamed "$stdout_id" instead. 

sub stdout { 
    my $self = shift; 

    return $stdout if defined $stdout; 

    $stdout = scalar(@filehandles);       ### CHANGED 
    my $stdout_fh = $self->dup_filehandle(\*STDOUT);  ### CHANGED 
    push @filehandles, $stdout_fh;       ### CHANGED 

    $self->autoflush($stdout_fh);       ### CHANGED 
    $self->autoflush(\*STDOUT); 

    return $stdout; 
} 

sub safe_print { 
    my $self = shift; 
    my $fh_id = shift;          ### CHANGED 
    my $fh = $filehandles[$fh_id];       ### CHANGED 

    local($\, $,) = (undef, ''); 
    print $fh @_; 
} 

Tôi có một cảm giác mạnh mẽ rằng bạn sẽ cần phải bằng cách nào đó cũng an toàn với danh sách ID, vì vậy có lẽ cần phải truy cập vào một số chỉ mục được chia sẻ thay vì $stdout = scalar(@filehandles);

+0

Tôi thấy bạn đang đi đâu. Tôi đã suy nghĩ về một cái gì đó như thế này. Một vấn đề là nếu một filehandle mới được mở trong một chủ đề, nó sẽ không được nhìn thấy bởi bất kỳ chủ đề khác hoặc phụ huynh. Tôi rất vui khi nói với người dùng "đừng làm điều đó". Tôi không nghĩ rằng thay đổi kết quả đầu ra trong một thread làm việc với Test :: Builder hiện tại anyway và không ai phàn nàn. – Schwern

+0

@Schwern - xem câu trả lời thứ hai của tôi. Tôi không chắc chắn rằng nó hoạt động tốt như thế nào – DVK

+0

Tôi nghĩ rằng tôi sẽ phải sử dụng một số biến thể về điều này, nhưng tôi có thể squirrel nó đi trong một phân lớp chủ đề cụ thể để sử dụng bình thường không bị ảnh hưởng bởi bletcherousness. – Schwern

5

Là một giải pháp thay thế cho mảng toàn cầu của tôi, đây là một cách tiếp cận khác từ Perlmonks:

http://perlmonks.org/?node_id=395513

Nó hoạt động bằng cách thực sự lưu trữ fileno (bộ mô tả tệp) của tệp thủ công. Dưới đây là code mẫu của mình dựa trên những gì BrowserUk gửi:

my $stdout; # Store the fileno, NOT filehandle. 
      # Should really be renamed "$stdout_fileno" instead. 

sub stdout { 
    my $self = shift; 

    return $stdout if defined $stdout; 

    my $stdout_fh = $self->dup_filehandle(\*STDOUT);  ### CHANGED 
    $stdout = fileno $stdout_fh;       ### CHANGED 

    $self->autoflush($stdout_fh);       ### CHANGED 
    $self->autoflush(\*STDOUT); 

    return $stdout; 
} 

sub safe_print { 
    my $self = shift; 
    my $fh_id = shift;          ### CHANGED 
    open(my $fh, ">>&=$fh_id")        ### CHANGED 
     || die "Error opening filehandle: $fh_id: $!\n";  ### CHANGED 

    local($\, $,) = (undef, ''); 
    print $fh @_; 
} 

caveat - như năm 2004, điều này đã có một lỗi mà bạn không thể đọc từ filehandle được chia sẻ từ> 1 chủ đề. Tôi đoán rằng viết là OK. Chi tiết cụ thể về cách làm việc ghi đồng bộ trên một filehandle chia sẻ (so với cùng Monk): http://www.perlmonks.org/?node_id=807540

+0

Thử tốt, nhưng tập tin được đề cập có thể không có fileno. Nó có thể được gắn, một ống, một refar vô hướng ... – Schwern

+0

@ Schwern - Tôi đã được ấn tượng rằng một đường ống có một bộ mô tả. Nếu có thứ gì đó bị trói, bạn bị vặn đôi - AFAIK bạn không thể sử dụng dữ liệu gắn với chủ đề :: được chia sẻ vì chia sẻ là một tie - phải sử dụng Thread :: Tie. – DVK

3

Nó chỉ xảy ra với tôi có hai giải pháp khả thi:

  1. Đặt filehandle bên ngoài đối tượng Streamer.
  2. Đặt đối tượng Streamer bên ngoài Trình định dạng.

@ gợi ý DVK là tất cả về làm 1.

Nhưng 2 là trong một số cách đơn giản hơn 1. Thay vì giữ đối tượng Streamer chính nó, Formatter có thể giữ một định danh cho các đối tượng Streamer. Nếu Streamer được thực hiện từ trong ra ngoài, điều đó xảy ra một cách tự nhiên!

Thật không may, các địa chỉ tham chiếu thay đổi giữa các chuỗi, thậm chí là các địa chỉ được chia sẻ. Điều này có thể được giải quyết với Hash::Util::FieldHash, nhưng đó là một điều 5.10 và tôi phải hỗ trợ 5.8. Có thể có thể kết hợp một thứ gì đó với nhau bằng cách sử dụng CLONE.

+1

Đối tượng :: InsideOut là luồng an toàn trên 5.8.1+ (http://search.cpan.org/~jdhedden/Object-InsideOut-3.84/lib/Object/InsideOut.pod#THREAD_SUPPORT) - có thể giúp bạn. CLONE có vẻ là một cách tiếp cận được khuyến nghị cho điều này (https://www.socialtext.net/perl5/inside_out_object) – DVK

+2

@DVK Cảm ơn, đó là thông tin tốt. Tôi không thể làm cho người đứng đầu cũng không đuôi của những gì Object :: InsideOut đang làm, nhưng tôi đã tìm thấy bài viết này của David vàng mà cuối cùng đã có một ví dụ sane của 'CLONE'. http://perlmonks.org/index.pl?node_id=483162 – Schwern

1

Dưới đây là những gì tôi vết thương với ...

package ThreadSafeFilehandle; 

use Mouse; 
use Mouse::Util::TypeConstraints; 

my %Filehandle_Storage; # unshared storage of filehandles 
my $Storage_Counter = 1; # a counter to use as a key 

# This "type" exists to intercept incoming filehandles. 
# The filehandle goes into %Filehandle_Storage and the 
# object gets the key. 
subtype 'FilehandleKey' => 
    as 'Int'; 
coerce 'FilehandleKey' => 
    from 'Defined', 
    via { 
     my $key = $Storage_Counter++; 
     $Filehandle_Storage{$key} = $_; 
     return $key; 
    }; 

has thread_safe_fh => 
    is   => 'rw', 
    isa   => 'FilehandleKey', 
    coerce  => 1, 
; 

# This converts the stored key back into a filehandle upon getting. 
around thread_safe_fh => sub { 
    my $orig = shift; 
    my $self = shift; 

    if(@_) {     # setting 
     return $self->$orig(@_); 
    } 
    else {      # getting 
     my $key = $self->$orig; 
     return $Filehandle_Storage{$key}; 
    } 
}; 

1; 

Sử dụng kiểu ép buộc đảm bảo rằng bản dịch từ filehandle để chìa khóa xảy ra ngay cả trong các nhà xây dựng đối tượng.

Tính năng này hoạt động nhưng có lỗi:

Mỗi đối tượng lưu trữ tập tin dư thừa. Nếu một loạt các đối tượng lưu trữ cùng một tập tin, chúng có thể chỉ lưu trữ một lần. Bí quyết sẽ là cách xác định cùng một tập tin. fileno hoặc refaddr là các tùy chọn.

Filehandle không bị xóa khỏi% Filehandle_Storage khi xóa đối tượng. Ban đầu tôi đặt một phương thức DESTROY để làm như vậy, nhưng vì thành ngữ nhân bản đối tượng là $clone = shared_clone($obj) $ handhandle của clone được chuyển vào thùng rác một lần $ obj nằm ngoài phạm vi.

Những thay đổi xảy ra ở trẻ em không được chia sẻ.

Đây là tất cả chấp nhận được cho mục đích của tôi mà sẽ chỉ tạo ra một số ít các đối tượng này cho mỗi quá trình.

0

Sau đó, một lần nữa, người ta có thể sử dụng https://metacpan.org/module/Coro nếu một người không có phản ứng dị ứng với trolldocs của nó.

+0

Cảm ơn, nhưng đó không phải là lựa chọn của tôi. Mô-đun của tôi phải làm việc với các chủ đề. – Schwern

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