Νήματα, διεργασίες και ασύγχρονη εκτέλεση — DedSec Project
Ασύγχρονη εκτέλεση — χαμένες ενημερώσεις και σωστό κλείδωμα Σενάριο και κριτήρια επιτυχίας Πενήντα ασύγχρονες εργασίες αυξάνουν έναν κοινό μετρητή κατά ένα. Το σωστό αποτέλεσμα είναι 50…
Ασύγχρονη εκτέλεση — χαμένες ενημερώσεις και σωστό κλείδωμα
Σενάριο και κριτήρια επιτυχίας
Πενήντα ασύγχρονες εργασίες αυξάνουν έναν κοινό μετρητή κατά ένα. Το σωστό αποτέλεσμα είναι 50, αλλά όταν η διαδικασία ανάγνωσης, αναμονής και εγγραφής διακόπτεται στη μέση, πολλές εργασίες χρησιμοποιούν την ίδια παλιά τιμή. Το πρόγραμμα αναπαράγει σκόπιμα το σφάλμα και στη συνέχεια προστατεύει ολόκληρη τη μεταβολή με asyncio.Lock. Χρησιμοποιεί έναν βρόχο συμβάντων, όχι ξεχωριστά νήματα λειτουργικού συστήματος.
Εκτελέσιμο εργαστήριο — ενσωματωμένη βιβλιοθήκη Python 3
Αποθήκευσε τον κώδικα ως 09_concurrency.py και εκτέλεσέ τον με Python 3. Χρησιμοποιεί μόνο τεχνητά δεδομένα και εμφανίζει PASS αν περάσουν οι έλεγχοι. Ο κώδικας είναι ίδιος και στις δύο γλωσσικές εκδόσεις.
import asyncio
async def unsafe():
state = {"value": 0}
async def job():
before = state["value"]
await asyncio.sleep(0)
state["value"] = before + 1
await asyncio.gather(*(job() for _ in range(50)))
return state["value"]
async def safe():
state, lock = {"value": 0}, asyncio.Lock()
async def job():
async with lock:
before = state["value"]
await asyncio.sleep(0)
state["value"] = before + 1
await asyncio.gather(*(job() for _ in range(50)))
return state["value"]
assert asyncio.run(unsafe()) == 1
assert asyncio.run(safe()) == 50
print("PASS async lock and deterministic lost update")
Τεχνική εξήγηση
Οι ταυτόχρονες εργασίες μπορούν να εναλλάσσονται χωρίς να εκτελούνται παράλληλα σε διαφορετικούς πυρήνες. Η await επιτρέπει σε άλλες εργασίες να προχωρήσουν. Αν πολλές διαβάσουν την ίδια παλιά τιμή, το κλείδωμα μόνο της τελικής ανάθεσης είναι πολύ αργά. Το κλείδωμα πρέπει να καλύπτει ανάγνωση, υπολογισμό και εγγραφή. Προστατεύει μόνο τις εργασίες που χρησιμοποιούν το ίδιο αντικείμενο κλειδώματος· δεν συγχρονίζει αυτόματα άλλες διεργασίες ή εγγραφές σε δίσκο.
Περιορισμένες ασκήσεις
Μετέφερε το κλείδωμα ώστε να καλύπτει μόνο την ανάθεση και έλεγξε αν ξαναεμφανίζεται η απώλεια. Αύξησε τις εργασίες σε 1000 και μέτρησε την ορθότητα. Ακύρωσε μία εργασία ενώ περιμένει κλείδωμα και έλεγξε ότι το async with δεν το αφήνει δεσμευμένο. Όταν τα δεδομένα αποθηκεύονται σε SQLite, χρησιμοποίησε συναλλαγές βάσης αντί για σκέτο κλείδωμα Python.
Διάγνωση και τεκμήρια επιτυχίας
Αν μία εργασία φαίνεται κολλημένη, βρες πού περιμένει και αν κρατά κλείδωμα ενώ εκτελεί άσχετη αναμονή. Μην χρησιμοποιείς time.sleep μέσα στον βρόχο συμβάντων. Το παράδειγμα αφορά αναμονές asyncio, όχι αυτόματα τον ίδιο μηχανισμό για νήματα.