2010-02-12 3 views
9

Следующий код содержит потенциальный тупик, но представляется необходимым: чтобы безопасно копировать данные в один контейнер из другого, оба контейнера должны быть заблокированы, чтобы предотвратить появление изменений в другом потоке.Приобретите замок на двух взаимных ошибках и избегайте взаимоблокировки

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

У Foo есть контейнер STL и «сделать копию» по существу состоит из использования std :: copy. Как заблокировать оба мьютекса без введения тупика?

ответ

15

Наденьте какой-то общий заказ на экземпляры foo и всегда приобретайте их замки в порядке возрастания или убывания, , например., foo1->lock(), а затем foo2->lock().

Другой подход - использовать функциональную семантику и вместо этого написать метод foo::clone, который создает новый экземпляр, а не сбивает существующий.

Если ваш код выполняет много блокировки, вам может потребоваться комплексный алгоритм устранения взаимоблокировки, такой как banker's algorithm.

+0

Даже как-то просто, как адреса этого против ОРЗ должно сработать. всегда запирайте сначала нижний адрес. –

+0

Клон будет работать только в том случае, если он не будет копировать, и я не думаю, что неявное совместное использование будет работать, но я посмотрю. Интересный подход Кайл. Я не вижу недостатков. – pomeroy

1

Как насчет этого?

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

Это безопасное исключение, а также хорошая потоковая безопасность. Чтобы быть 100% -ной нитью, вам нужно будет просмотреть весь путь кода и повторить повторный просмотр с другим набором глаз, после этого снова просмотрите его ...

-1

Чтобы избежать тупика, вероятно, это лучше всего, подождать до как ресурсы могут быть заблокированы:

Dont знаю, какой мьютекс API вы используете так вот некоторый произвольный код псевдо, предположим, что can_lock() только проверяет, если он может заблокировать мьютекс, и что try_lock() возвращает истину, если он сделал замок, и ложь , если мьютекс уже заблокирован кем-то другим.

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

Чтобы избежать тупика, лучше всего представить живую погоду? И вращение, используя 100% процессор? – bk1e

-1

Вы можете попробовать блокировки оба мьютексы в то же самое время, используя scoped_lock или auto_lock .... как банковский перевод сделать ...

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

    //do transaction. 
} 
Смежные вопросы