2013-12-24 12 views
4

В некоторых учебнике я видел реализацию таких спин блокировкиC++ приказывать

class spin_lock 
{ 
    atomic<unsigned int> m_spin ; 

public: 
    spin_lock(): m_spin(0) {} 
    ~spin_lock() { assert(m_spin.load(memory_order_relaxed) == 0);} 

    void lock() 
    { 
     unsigned int nCur; 
     do { nCur = 0; } 
     while (!m_spin.compare_exchange_weak(nCur, 1, memory_order_acquire)); 
    } 
    void unlock() 
    { 
     m_spin.store(0, memory_order_release); 
    } 
}; 

нам действительно нужно memory_order_acquire/memory_order_release теги для compare_exchange_weak и store операции соответственно делать? Или memory_order_relaxed является достаточным в этом случае, поскольку нет синхронных отношений?

Обновление: Спасибо за объяснения! Я рассматривал spin_lock без контекста, в котором он используется.

+3

«нет синхронизации» С отношением «Да, есть. Например: предположим, что spin_lock защищает общий ресурс, а поток A использует spin_lock.lock() для получения ресурса, тогда как поток B отпирает блокировку. Для предотвращения переупорядочения инструкций требуется порядок памяти. –

+0

Я бы реализовал спин-блокировки с помощью ['atomic_flag'] (http://en.cppreference.com/w/cpp/atomic/atomic_flag), так как это единственный гарантированный переносимый без блокировки атомный тип. – Casey

ответ

3

Что касается memory_order_acquire/memory_order_release: рассмотрите общие данные (ячейки памяти или ресурсы памяти a.k.a.), которые вы используете для защиты блокировки. Я назову это «защищенные данные».

Код должен гарантировать, что когда функция lock() вернется, любой доступ к защищенным данным вызывающим будет считывать допустимые значения (то есть не устарелые значения). memory_order_aquire гарантирует, что соответствующий барьер памяти для аксессуаров будет вставлен так, что последующие чтения защищенных данных (через локальный кеш процессора) будут действительными. Аналогично, когда вызывается unlock(), memory_order_release необходимо для обеспечения того, чтобы соответствующий барьер памяти был вставлен так, чтобы другие тайники были правильно синхронизированы.

Некоторые процессоры не нуждаются в барьерах для доступа/выхода, и для теоретического примера может потребоваться только полный барьер в замке(). Но модель параллелизма C++ должна быть достаточно гибкой, чтобы поддерживать множество разных процессорных архитектур.

memory_order_relaxed используется в деструкторе, потому что это просто проверка работоспособности, чтобы убедиться, что блокировка в настоящее время не удерживается. Уничтожение блокировки не передает семантику синхронизации вызывающему.

10

Да memory_order_acquire/memory_order_release не требуется.

Вы используете блокировки для защиты общих данных между несколькими потоками. если вы не используете memory_order_release в методе unlock, любая запись на общих данных может быть переупорядочена после метода unlock. Также, если вы не используете memory_order_acquire в методе lock, любой прочитанный на общих данных может быть переупорядочен до lock. поэтому вам нужно acquire/release для защиты общих данных между потоками.

spinLock.lock() // use acquire here, so any read can't reordered before `lock` 

// Writes to shared data 

spinLock.unlock() // use release here, so any write can't reordered after `unlock` 

acquire/release с всеми записями на общие данные будут видны нитям, которые запирают спинлок.

 Смежные вопросы

  • Нет связанных вопросов^_^