08/03/2009

Prymitywy synchronizacyjne - jak dobrze je znamy?

Home

Ostatnio zadałem kilku osobom następujące pytanie. Spośród wymienionych poniżej prymitywów synchronizacyjnych wybierz, te które mogą zostać użyte do synchornizacji między-procesowej:
  • Klasa Monitor
  • Klasa Mutex
  • Klasa Semaphore
  • Słowo kluczowe lock
  • Klasa AutoResetEvent
  • Klasa ManualResetEvent
Sądziłem, że pytanie należy raczej do tych z kategorii łatwiejszych. Odpowiedź na nie przysporzyła jednak niestety sporo kłopotów. Na poniższej liście zostawiłem tylko elementy stanowiące poprawną odpowiedź:
  • Klasa Mutex
  • Klasa Semaphore
  • Klasa AutoResetEvent
  • Klasa ManualResetEvent
Czym różnią się wybrane prymitywy synchronizacyjne od pozostałych? Moim zdaniem można wymienić cztery zasadnicze różnice:
  • Po pierwsze klasy te dziedziczą pośrednio po klasie MarshalByRefObject i w związku z tym instancje tych klas mogą przekraczać granice po między dziedzinami aplikacyjnymi (ang. application domain).
  • Po drugie klasy te są wywiedzione z klasy WaitHandle i w związku można ich używać w podobny sposób chociaż mają zupełnie inną semantykę. W szczególności można sobie wyobrazić scenariusz, w którym wątek oczekuje równocześnie na zwolnienie semafora, mutexa i zapalenie się sygnału/zdarzenia (należy użyć metody statycznej WaitHandle.WaitAll())
  • Po trzecie klasy te stanowią w rzeczywistości opakowanie na natywne/systemowe prymitywy synchronizacyjne udostępnione w systemie operacyjny co generalnie powoduje, że są wolniejsze.
  • Po czwarte i najważniejsze z perspektywy pytania te cztery prymitywy synchronizacyjne mogą zostać nazwane. Inaczej mówiąc, w jednym procesie możemy utworzyć semafor o nazwie "MY SEMAPHORE", a w drugim procesie uzyskać dostęp do tego samego semafora posługując się podaną nazwą. W tym celu można użyć metody statycznej Semaphore.OpenExisting() lub z odpowiedniego konstruktora.
Moim zdaniem wiedza o sposobie działania, semantyce poszczególnych prymitywów synchronizacyjnych jest niezwykle ważna dla każdego programisty bez względu z jaką technologią pracuje. Błędy związane z synchronizacją są jednymi z najtrudniejszych do wykrycia i naprawienia, a wiele z nich można by unikać gdyby wszyscy programiści znali dokładnie narzędzie swojej pracy.

2 comments:

Anonymous said...

Cześć!
Bardzo ciekawy post, ale nie bardzo rozumiem dlaczego natywne mechanizmy synchronizacji systemu operacyjnego są wolniejsze niż mechanizmy CLR?
Pozdrawiam
Agnieszka

Michał Komorowski said...

Sądzę, że powodów jest kilka. Po pierwsze komunikacja z kodem natywnym wymaga dodatkowych nakładów np.: serializowania argumentów wywołań funkcji (w obie strony), dalej komunikując się z kodem natywnym należy zapewnić, że GC nie poprzesuwa w tym czasie obiektów w pamięci co też ma swój koszt. Nie bez znaczenia jest fakt, że natywne prymitywy mogą mieć nazwę. Przy tworzeniu nowego prymitywu należy najpierw sprawdzić czy taki już istnieje. Po trzecie to co udostępnia CLR umożliwia tylko synchronizację wątków. Mechanizmy systemowe natomiast pozwalają na synchronizację międzyprocesową. Jest to trudniejsze, więc wymaga więcej zasobów. Po czwarte, ale mogę się mylić, użycie prymitywów udostępnionych przez OS wymaga przełączenia w tryb jądra, a potem z powrotem w tryb użytkownika co również kosztuje.

Post a comment