Синхронизация доступа к возвращаемому значению - PullRequest
14 голосов
/ 08 июля 2010

Рассмотрим следующую функцию-член C ++:

 size_t size() const
 {
    boost::lock_guard<boost::mutex> lock(m_mutex);
    return m_size;
 }

Целью здесь является не синхронизация доступа к закрытой переменной-члену m_size, а просто чтобы убедиться, что вызывающая сторона получает действительное значение для m_size,Цель состоит в том, чтобы не дать функции возвращать m_size в то же время, когда какой-то другой поток модифицирует m_size.

Но есть ли потенциальные условия гонки, связанные с вызовом этой функции?Я не уверен, что блокировка стиля RAII здесь достаточна для защиты от состояния гонки.Предположим, что деструктор блокировки вызывается до , возвращаемое значение функции помещается в стек?

Нужно ли что-то вроде следующего, чтобы гарантировать безопасность потока?

 size_t size() const
 {
    size_t ret;

    {
      boost::lock_guard<boost::mutex> lock(m_mutex);
      ret = m_size;
    }

    return ret;
 }

Ответы [ 3 ]

12 голосов
/ 08 июля 2010

Обе ваши примерные конструкции будут делать то, что вы ищете.Следующая информация из стандарта поддерживает поведение, которое вы ищете (даже в первом примере):

12.4 / 10 Деструкторы:

Деструкторы вызываются неявно ... дляпостроенный объект с автоматической продолжительностью хранения (3.7.2) при выходе из блока, в котором создан объект.

И, 6.6 / 2 Операторы перехода (из которых return - один):

При выходе из области (хотя и выполненной) деструкторы (12.4) вызываются для всех построенных объектов с автоматической продолжительностью хранения (3.7.2) (именованных объектов или временных объектов), которые объявлены в этой области,в обратном порядке их объявления.Передача из цикла, из блока или обратно после инициализированной переменной с автоматическим сроком хранения включает в себя уничтожение переменных с автоматическим сроком хранения, которые находятся в области действия в точке, перенесенной из, но не в точке, переданной в.

Таким образом, в точке return переменная lock находится в области видимости, и поэтому dtor не был вызван.После выполнения return вызывается dtor для переменной lock (освобождая блокировку).

3 голосов
/ 08 июля 2010

Ваш первый вариант безопасен, однако вы не можете полагаться на то, что возвращаемое значение будет согласованным в течение любого периода времени. Например, я не использую это возвращаемое значение в цикле for для итерации по каждому элементу, потому что реальный размер может измениться сразу после возврата.

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

Деструктор вызывается после оператора return. Возьмите этот эквивалентный пример:

#include <assert.h>

class A
{
public:
    ~A()
    {
        x = 10;
    }
    int x;
};

int test()
{
    A a;
    a.x = 5;
    return a.x;
}

int main(int argc, char* argv[])
{
    int x = test();
    assert(x == 5);
    return 0;
}
1 голос
/ 08 июля 2010

Ваш исходный код в порядке - деструктор будет вызываться после сохранения возвращаемого значения.Это тот самый принцип, на котором работает RAII!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...