Синхронизация доступа к возвращаемому значению

рассмотрим следующую функцию-член 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 ответов


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

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

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

и, 6.6 / 2 заявления прыжка (из которых return is один):

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

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


ваш первый вариант является безопасным, однако вы не можете полагаться на это возвращаемое значение, чтобы быть последовательным в течение любого периода времени. Я имею в виду, например, не используйте это возвращаемое значение в цикле 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;
}

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