Διάφορα

lock#

Τοποθετεί προτρεπτικό κλείδωμα σε κοινόχρηστη μεταβλητή, πίνακα, πίνακα κατακερματισμού ή υπορουτίνα μέχρι το κλείδωμα να βγει εκτός εμβέλειας.

Η lock είναι το πρωτογενές συγχρονισμού του μοντέλου διερμηνέα-threads της Perl (use threads; use threads::shared). Όταν το THING είναι μεταβλητή που έχει δηλωθεί ως :shared - ή αναφορά σε κοινόχρηστο σύνθετο - η lock αποκτά προτρεπτικό mutex σε αυτό το δεδομένο για το υπόλοιπο του περικλείοντος μπλοκ. Όταν το μπλοκ εξέλθει, το κλείδωμα απελευθερώνεται. Οποιοδήποτε άλλο thread καλεί lock στο ίδιο δεδομένο μπλοκάρει μέχρι ο κάτοχος να το απελευθερώσει.

Το κλείδωμα είναι προτρεπτικό: σταματάει άλλα threads που επίσης καλούν lock στην ίδια μεταβλητή, τίποτα άλλο. Ένα thread που απλώς διαβάζει ή γράφει την κοινόχρηστη μεταβλητή χωρίς να την κλειδώνει δεν μπλοκάρεται και δεν σειριοποιείται. Η lock είναι σύμβαση, όχι φραγμός.

Σύνοψη#

lock $shared_scalar
lock @shared_array
lock %shared_hash
lock &shared_sub
lock $ref_to_shared_aggregate

Τι επιστρέφεται#

Το ίδιο το όρισμα:

  • Για βαθμωτό, η βαθμωτή τιμή.

  • Για πίνακα, πίνακα κατακερματισμού ή υπορουτίνα, αναφορά σε αυτό.

Η τιμή επιστροφής σπάνια χρησιμοποιείται. Η lock καλείται για την παρενέργειά της (απόκτηση του mutex), όχι για την τιμή της.

Το κλείδωμα έχει εμβέλεια μπλοκ, όχι εμβέλεια δήλωσης#

Το κλείδωμα απελευθερώνεται όταν το εσώτατο περικλείον μπλοκ εξέλθει, όχι όταν τελειώσει η δήλωση lock. Δύο συνέπειες:

  • Η lock μέσα σε μπλοκ { ... } σειριοποιεί μόνο τη διάρκεια αυτού του μπλοκ. Η έξοδος από το μπλοκ - με φυσιολογική ολοκλήρωση, return, last, die, οτιδήποτε - απελευθερώνει το κλείδωμα.

  • Δεν μπορείτε να απελευθερώσετε ένα κλείδωμα νωρίτερα. Δεν υπάρχει unlock. Αν χρειάζεστε στενότερο κρίσιμο τμήμα, εισαγάγετε ένα σφιχτότερο μπλοκ { ... }:

    {
        lock %shared_hash;
        $shared_hash{$key} = $value;
    }   # lock released here
    do_unlocked_work();
    

Ασθενής δεσμευμένη λέξη#

Η lock είναι ασθενής δεσμευμένη λέξη. Αν υπάρχει υπορουτίνα με όνομα lock εντός εμβέλειας στο σημείο της κλήσης (δηλωμένη ή εισαγμένη πριν το σημείο κλήσης), καλείται αυτή η υπορουτίνα αντί για την ενσωματωμένη. Αυτό κάνει την lock ασφαλή να χρησιμοποιείται ως όνομα μεθόδου ή συνάρτησης σε κώδικα που δεν χρησιμοποιεί threads. Για να επιβάλλετε την ενσωματωμένη όταν μια ομώνυμη sub είναι εντός εμβέλειας, καλέστε την μέσω του πακέτου της: CORE::lock($thing).

Παραδείγματα#

Σειριοποιήστε μια ενημέρωση πίνακα κατακερματισμού μεταξύ threads:

use threads;
use threads::shared;

my %counts :shared;

sub bump {
    my $key = shift;
    lock %counts;
    $counts{$key}++;
}

Κλειδώστε ένα σύνθετο μέσω αναφοράς. Η lock ακολουθεί ένα επίπεδο αναφοράς για να βρει το δεδομένο που θα κλειδώσει:

my @queue :shared;
my $queue_ref = \@queue;

sub enqueue {
    my $item = shift;
    lock $queue_ref;        # locks @queue
    push @queue, $item;
}

Στενέψτε το κρίσιμο τμήμα με ένα εσωτερικό μπλοκ ώστε άσχετη εργασία να τρέχει χωρίς κλείδωμα:

my $result;
{
    lock %cache;
    $result = $cache{$key};
}
expensive_post_processing($result);   # runs without the lock

Χωρίς use threads::shared, η lock είναι no-op. Αυτό την καθιστά ακίνδυνη ώστε να αφεθεί σε κώδικα που μπορεί να τρέχει με ένα μόνο thread:

# no `use threads::shared` here
lock $x;                    # does nothing, does not warn

Οριακές περιπτώσεις#

  • Μη κοινόχρηστη μεταβλητή: η lock σε μεταβλητή που δεν έχει δηλωθεί :shared και δεν είναι αναφορά σε κοινόχρηστα δεδομένα δεν κάνει κάτι χρήσιμο. Στο upstream μπορεί σε κάποιες περιπτώσεις να εκπέμψει σφάλμα· σε άλλες είναι σιωπηρά no-op. Μη βασίζεστε στη lock για να πιάσετε το σφάλμα «ξέχασα να μοιραστώ».

  • Αναφορά έναντι σύνθετου: τόσο η lock \%h όσο και η lock %h κλειδώνουν τον %h. Η lock αποαναφέρει ένα επίπεδο για να βρει το κοινόχρηστο δεδομένο.

  • Αναδρομικό κλείδωμα μέσα σε ένα thread: το ίδιο thread μπορεί να ξανααποκτήσει ένα κλείδωμα που ήδη κρατάει· το mutex είναι αναδρομικό ανά thread. Άλλο thread παρ” όλα αυτά μπλοκάρει.

  • Καμία unlock: η απελευθέρωση πριν την έξοδο μπλοκ απαιτεί αναδιάρθρωση σε στενότερη εμβέλεια. Τα μοτίβα undef $lock_guard από άλλες γλώσσες δεν ισχύουν - η lock δεν είναι τιμή που αποθηκεύετε.

  • Αμφισημία ασθενούς δεσμευμένης λέξης: μια sub lock που δηλώθηκε ή εισήχθη νωρίτερα υπερισχύει της ενσωματωμένης. Χρησιμοποιήστε CORE::lock για αποσαφήνιση.

  • Όχι μεταξύ διεργασιών: η lock συντονίζει threads μέσα σε μία διεργασία διερμηνέα. Για κλείδωμα σε επίπεδο αρχείου μεταξύ διεργασιών χρησιμοποιήστε την flock· για προτρεπτικά κλειδώματα συστήματος αρχείων αυτοί είναι ξεχωριστοί μηχανισμοί χωρίς αλληλεπίδραση.

Διαφορές από το upstream#

  • Η pperl δεν υλοποιεί διερμηνέα-threads. Ο opcode lock αναγνωρίζεται από τον αναλυτή αλλά δεν έχει επίδραση στον χρόνο εκτέλεσης - συμπεριφέρεται ακριβώς όπως η upstream lock σε build χωρίς threads::shared: σιωπηρό no-op που επιστρέφει το όρισμά του. Κώδικας που χρησιμοποιεί τη lock αμυντικά (όπως συνιστά η τεκμηρίωση upstream για συμβατότητα με ένα μόνο thread) τρέχει χωρίς αλλαγή. Κώδικας που εξαρτάται από τη lock για πραγματικό συγχρονισμό δεν θα συγχρονιστεί υπό pperl.

  • Η συνοδευτική διανομή threads::shared δεν είναι διαθέσιμη στην pperl· το use threads::shared αποτυγχάνει κατά τη μεταγλώττιση. Ο παραλληλισμός στην pperl εκτίθεται μέσω αυτο-παραλληλοποίησης JIT (Rayon) σε συγκεκριμένα σχήματα βρόχου, όχι μέσω ορατών στον χρήστη ithreads.

Δείτε επίσης#

  • flock - προτρεπτικό κλείδωμα σε filehandle, που συντονίζει μεταξύ διεργασιών αντί για threads

  • wait - συγκομιδή θυγατρικής διεργασίας· το ισοδύναμο σε επίπεδο διεργασίας της αναμονής για άλλη μονάδα εκτέλεσης

  • fork - το άλλο πρωτογενές ταυτοχρονικότητας στον πυρήνα της Perl, χωρίς προεπιλεγμένη κοινόχρηστη μνήμη

  • our - δηλώστε μεταβλητή με εμβέλεια πακέτου· συνήθως συνδυάζεται με :shared σε κώδικα με threads

  • local - σύνδεση με δυναμική εμβέλεια, μερικές φορές μπερδεύεται με τη lock επειδή και οι δύο είναι οριοθετημένες σε εμβέλεια και αμφότερες επαναφέρουν στην έξοδο μπλοκ