2010-02-12 8 views
9

Poniższy kod zawiera potencjalny zakleszczenie, ale wydaje się konieczny: aby bezpiecznie skopiować dane do jednego kontenera z drugiego, oba kontenery muszą być zablokowane, aby zapobiec zmianom w innym wątku.Uzyskiwanie blokady na dwóch muteksach i unikanie zakleszczenia

void foo::copy(const foo & rhs) 
{ 
    pMutex->lock(); 
    rhs.pMutex->lock(); 
    // do copy 
} 

Foo ma kontener STL, a "wykonaj kopię" zasadniczo polega na użyciu std :: copy. Jak mogę zablokować oba muteksy bez wprowadzania impasu?

Odpowiedz

15

Nałożyć pewnego rodzaju łączną kolejność na wystąpienia foo i zawsze nabywać ich zamki w kolejności rosnącej lub malejącej, np. , np., foo1->lock(), a następnie foo2->lock().

Innym podejściem jest użycie funkcjonalnej semantyki i napisanie metody foo::clone, która tworzy nową instancję, zamiast nawijać istniejącą.

Jeśli twój kod wykonuje wiele blokad, może być potrzebny złożony algorytm unikania zakleszczenia, taki jak banker's algorithm.

+0

Nawet coś tak prostego jak adresy VS RHS pracowałbym. zawsze najpierw blokuj ten z niższym adresem. –

+0

Klon działałby dobrze tylko wtedy, gdyby nie był kopiowany, i nie sądzę, że niejawne udostępnianie będzie działać, ale przyjrzę się. Interesujące podejście Kyle. Nie widzę żadnych wad. – pomeroy

1

Co powiesz na to?

void foo::copy(const foo & rhs) 
{ 
    scopedLock lock(rhs.pMutex); // release mutex in destructor 
    foo tmp(rhs); 
    swap(tmp); // no throw swap locked internally 
} 

Jest to wyjątek bezpieczny, a także bezpieczny dla wątków. Aby być w 100% wątkiem, musisz przejrzeć całą ścieżkę kodu, a następnie ponownie ją przejrzeć przy użyciu innego zestawu oczu, a następnie ponownie przeanalizować ...

-1

Aby uniknąć zakleszczenia, prawdopodobnie najlepiej poczekać do zarówno zasoby mogą być zablokowane

nie wiem który mutex API używasz więc tutaj jest jakaś arbitralna pseudokod, załóżmy, że can_lock() tylko sprawdza, czy można go zablokować mutex i że try_lock() zwraca true jeśli tak blokadę i false , jeśli muteks jest już zablokowany przez kogoś innego.

void foo::copy(const foo & rhs) 
{ 
    for(;;) 
    { 
     if(! pMutex->cany_lock() || ! rhs.pMutex->cany_lock()) 
     { 
      // Depending on your environment call or dont call sleep() 
      continue; 
     } 
     if(! pMutex->try_lock()) 
      continue; 
     if(! rhs.pMutex->try_lock()) 
     { 
      pMutex->try_lock() 
      continue; 
     } 
     break; 
    } 
    // do copy 
} 
+2

Aby uniknąć zakleszczenia, najlepiej wprowadzić blokadę na żywo? I spin, używając 100% procesora? – bk1e

-1

Można spróbować blokowania zarówno muteksy jednocześnie stosując scoped_lock lub auto_lock .... jak zrobić przelew ...

void Transfer(Receiver recv, Sender send) 
{ 
    scoped_lock rlock(recv.mutex); 
    scoper_lock slock(send.mutex); 

    //do transaction. 
} 
Powiązane problemy