2016-04-30 8 views
1

Ich versuche, Code zu optimieren, der eine Datei lädt und die Daten in Perl analysiert.Perl: Build komplexer Objektbaum mit mehreren Threads

Hintergrund

  • Die Daten endet in einem komplizierten Objekt Baum.
  • Das oberste Objekt ist ein gesegnetes Paket.
  • Einige der verschachtelten Objekte werden als ein anderes gesegnetes Paketart-Call-Element neugestaltet.
  • Der erste Durchlauf trennt die Binärdaten in Einheiten und Segmente innerhalb einer Einheit, die alle in mehreren Arrays gespeichert sind.
  • Es könnte 20 oder 50 Einheiten mit je 8 Segmenten geben.
  • Der zweite Durchlauf führt die Decodierung der binären Daten durch und ist optimiert für die Geschwindigkeit.

Ansatz mit Gewinde

  • Ich versuche, die Module Threads und Threads zu verwenden :: geteilt.
  • Ich möchte, dass jeder Thread eine Teilmenge der Einheiten verarbeitet und die Daten in einen gemeinsamen Objektbaum auffüllt.
  • Ich bin auf der Suche nach Beispielcode, der zeigt, wie gesegnete Objekte, die von einem beliebigen Threadkontext zugewiesen und gesegnet werden können, in einen gemeinsamen Objektbaum eingefügt werden. Und dann ist von dem Hauptthread aus auf die Daten für Datensuchvorgänge zu gehen. Die Decodierungsthreads werden zurückkehren, sobald die Decodierung abgeschlossen ist.

  • Ich habe Schwierigkeiten zu sehen, wie mehrere Threads Objekte in einen gemeinsamen Objektbaum einfügen, indem Sie das threads: shared module verwenden. Insbesondere wenn Objekte vom Typ Item aus einem Thread-Kontext gesegnet sind. Die Klassen- (Paket-) Funktionen sind nicht an das Objekt gebunden, wie ich es verstehe.
  • Ich erkenne, dass an bestimmten Punkten im Code der Code die threads :: shared :: lock() -Funktion verwenden muss, bevor Objekte zum Objektbaum hinzugefügt werden.
  • Insbesondere die verschachtelten gesegneten Item-Objekte würden von jedem Thread-Kontext zugewiesen werden.
  • Die threads :: shared documentation sagt "Beachten Sie, dass es oft nicht ratsam ist, ein Objekt zu teilen, es sei denn, die Klasse selbst wurde geschrieben, um die Freigabe zu unterstützen".
    • Gibt es einen Beispielcode, der zeigt, wie dies zu erreichen ist?

Die Dokumentation sagt auch „Objekt destructor mehrmals aufgerufen werden kann, eine für jeden Anwendungsbereich Ausgang des Threads“. Wie wird das richtig gehandhabt?

Dank J.R.

Antwort

3

OK, also ein bisschen Rückzieher - threads::shared wirklich bedeutet ‚einzelne Datenstrukturen‘ und nicht wirklich kompliziertere Dinge unterstützen.Das liegt daran, dass Sie, wenn Sie "fädeln", tatsächlich separate Programminstanzen mit (teilweise) gemeinsamem Speicherplatz erstellen, aber praktisch ist jeder "Thread" ohnehin ein separates Programm.

So wird die Unterstützung der Freigabe in einem Objekt wirklich ziemlich unordentlich. Ich habe einen besseren Ansatz gefunden, um ... nicht. Verwenden Sie Thread::Queue, um Daten zwischen Threads zu übergeben, und über einen Thread, der die Ergebnisse sortiert. Wenn Sie kompliziertere Datenstrukturen übergeben müssen, können Sie Storable und freeze/thaw verwenden, um das Objekt zu serialisieren, und enqueue es.

Auf diese Weise müssen Sie sich keine Gedanken über gestaffelte verschachtelte Datenstrukturen machen - und es ist sehr wahrscheinlich, dass Sie, weil es keine "tiefe Freigabe" -Option für ein Objekt gibt - Sie müssen explizit share jedes interne Array/Hash (Referenz).

So würde ich es so angehen:

#!/usr/bin/perl 
use strict; 
use warnings; 

package Test_Object; 

sub new { 
    my ($class, $id) = @_; 
    my $self = {}; 
    $self->{id} = $id; 
    bless $self, $class; 
    return $self; 
} 

sub set_result { 
    my ($self, $result_code) = @_; 
    $self->{result} = $result_code; 
} 

sub get_id { 
    my ($self) = @_; 
    return $self->{id}; 
} 

sub get_result { 
    my ($self) = @_; 
    return $self->{result}; 
} 

package main; 

use strict; 
use warnings qw/ all /; 

use threads; 
use Thread::Queue; 
use Storable qw/ freeze thaw/; 

my $work_q = Thread::Queue->new(); 
my $result_q = Thread::Queue->new(); 

sub worker { 
    my $tid = threads->self->tid; 
    print "$tid: starting\n"; 
    while (my $item = $work_q->dequeue()) { 
     my $object = thaw($item); 
     print "$tid: got object with ID of ", $object->get_id, "\n"; 
     $object->set_result($object->get_id . " : $tid"); 
     $result_q->enqueue(freeze $object); 
    } 
} 

sub collator { 
    while (my $result = $result_q->dequeue) { 
     my $object = thaw $result; 
     print "Collator got object with result code of ", $object->get_result, 
     "\n"; 
    } 

    ## do something with collated wossnames - pass back to main maybe? 
} 

my @workers; 
for (1 .. 5) { 
    my $thr = threads->create(\&worker); 
    push @workers, $thr; 
} 

my $collator = threads->create(\&collator); 

for (1 .. 200) { 
    my $work_object = Test_Object->new($_); 
    $work_q->enqueue(freeze $work_object); 
} 

$work_q->end; 
foreach my $thr (@workers) { 
    $thr->join; 
} 

$result_q->end; 
foreach my $thr (threads->list) { 
    $thr->join; 
} 
+0

ich die Antwort zu schätzen wissen. Die Dokumentation für "Thread :: Warteschlange" besagt, dass die Übergabe von Objekten in Warteschlangen möglicherweise nicht funktioniert, wenn die Klassen der Objekte die Freigabe nicht unterstützen. Weitere Informationen finden Sie unter BUGS UND LIMITATIONS in threads :: shared. Das Übergeben von Array/Hash-Referenzen, die Objekte enthalten, funktioniert für Perl möglicherweise nicht vor 5.10.0. Es sieht so aus, als ob ich mit meinen gesegneten Objekten die gleichen Einschränkungen habe. Auch diese Anwendung ist leider immer noch auf Perl 5.8.8. –

+0

Ich kann nicht herausfinden, wie man mit diesem Kommentareditor Zeilenumbrüche erstellt. –

+0

Sie übergeben keine Objekte. Sie übergeben serialisierte Objekte als Skalare. – Sobrique