programowanieProgramista Python

Co się dzieje podczas przekazywania złożonych obiektów (na przykład list słowników) między wątkami lub procesami w Pythonie? O czym ważne jest, aby pamiętać podczas programowania aplikacji wielowątkowych i wieloprocesowych związanych z kolekcjami mutowalnymi?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Pythonie przy pracy z wątkami obiekty w pamięci są dostępne dla wszystkich wątków bez kopiowania. Jednak przy pracy z procesami przez multiprocessing standardowe obiekty Pythona (na przykład listy, słowniki) są przekazywane między procesami przez serializację (zwykle przez pickle), a każdy proces otrzymuje swoją kopię obiektu. Zmiana obiektu w jednym procesie nie odbija się na kopii w innym procesie.

Kluczowe różnice:

  • Wielowątkowość (threading): Wszystkie wątki działają w wspólnej przestrzeni adresowej. Jakiekolwiek obiekty (w tym zagnieżdżone kolekcje) są dzielone.
  • Wieloprocesowość (multiprocessing): Każdy proces ma swoją pamięć; obiekty są przekazywane przez serializację. Do wymiany między procesami należy używać specjalnych struktur, na przykład multiprocessing.Manager().dict().

Przykład:

# Wieloprocesowość from multiprocessing import Process, Manager def worker(d): d['a'] = 42 if __name__ == '__main__': # Zwykły dict nie działa między procesami managed_dict = Manager().dict() p = Process(target=worker, args=(managed_dict,)) p.start(); p.join() print(managed_dict['a']) # 42
  • Gdyby używany był zwykły dict, zmiany w procesie nie byłyby widoczne w głównym procesie.

Pytanie podchwytliwe.

Kiedy edytowanie zwykłej listy (list) w jednym procesie odbije się w innym procesie?

Odpowiedź:

Nigdy, jeśli pracujesz z multiprocessing. Zwykłe obiekty Pythona nie są dzielone między procesami. Aby je dzielić, należy używać specjalnych prymitywów — obiektów Manager (na przykład Manager().list(), Manager().dict()), które synchronizują stan między procesami.

Przykłady rzeczywistych błędów spowodowanych nieznajomością subtelności tego tematu.


Historia

Zespół programistów web crawlera zainicjował kolejki zadań jako listy i dzielił je między procesami przez multiprocessing. Zadania "ginęły", ponieważ procesy nie widziały nawzajem swoich aktualizacji. Naprawiono to na użycie Manager().list().


Historia

W serwisie analitycznym próbowano zbierać logi w zagnieżdżonych dictach wewnątrz wątków. W przypadku scenariuszy o wysokim obciążeniu doprowadziło to do warunków wyścigu i uszkodzenia danych z powodu braku blokad.


Historia

W serwisie ETL podczas próby zebrania zebranych podczas etapu przetwarzania obiektów danych w wielu procesach odkryto duplikację/utrata danych: programiści nie uwzględnili, że każdy proces pracował na swojej kopii struktury, a nie na wspólnej.