Double-Checked Locking

Z funus.net

Doble-Checked Locking (DLC), czy też Double-Checked Locking Pattern, to wzorzec projektowy mający na celu ograniczenie konieczności synchronizacji w programie wielowątkowym, często wykorzystywany w implementacji wzorca singletonu, w którym utworzenie instancji następuje przy pierwszym użyciu.

Niech szablon klasy implemnetującej singleton wygląda następująco:

template <typename T> class Singleton
{
public:
  static T* instance();
 
private:
  static T* theInstance;
};

W podejściu podstawowym implementacja funkcji instance() mogłaby wyglądać następująco:

template <typename T> T* Singleton<T>::instance()
{
  Mutex mutex;  // załóż lock, mniejsza o implementację
  if (!theInstance)
    theInstance = new T;
  return theInstance;
}  // destruktor obiektu mutex zwalnia lock

Jak widać, każdy dostęp do zmiennej theInstance jest synchronizowany. Jest to zupełnie bezpieczne ale nieefektywne. Dlatego parę osób wpadło na coś, co zostało nazwane właśnie DLC:

template <typename T> T* Singleton<T>::instance()
{
  if (!theInstance) {
    Mutex mutex;
    if (!theInstance)
      theInstance = new T;
  }
  return theInstance;
}

Czyli najpierw sprawdzamy bez synchronizacji, czy theInstance jest zerowy, i dopiero jeśli jest to zakładamy mutex, ponownie sprawdzamy (bo inny wątek mógł w międzyczasie zmienić), i jeśli wskaźnik nadal jest pusty, tworzymy instancję obiektu.

Niestety, pomysł ten okazał się nader naiwny, ponieważ nie wzięto pod uwagę szeregu czynników, które sprawiają, że DLC będzie działać nieprawidłowo:

  • kwestii optymalizacji kodu przez kompilator, który może poprzestawiać instrukcje; ten problem występuje np. w języku Java (przynajmniej do wersji 1.5);
  • kwestii różnych modelów spójności pamięci w systemach wieloprocesorowych.

Jak pokazali Scott Meyers i Andrei Alexandrescu, drugi z powyższych problemów da się w C++ rozwiązać odpowiednio stosując bariery pamięci (membars), a pierwszy można próbować wyeliminować przy pomocy słowa kluczowego volatile. Niestety, w praktyce często można spotkać użycie wadliwej, "naiwnej" implementacji DLC, czego przykładem może być singleton w bibliotece ACE; jego wykorzystanie na maszynach z luźniejszym modelem spójności pamięci prowadzi do przykrych i trudnych do wykrycia błędów. No cóż... Douglas Schmidt, jeden z głównych autorów bibliteki ACE, uważa DLC za pożyteczną optymalizację.

[edytuj] Lektura