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

Рассмотрите следующую функцию членства 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;
 }
14
задан Channel72 8 July 2010 в 00:56
поделиться

3 ответа

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

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

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

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

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

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

12
ответ дан 1 December 2019 в 13:58
поделиться

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

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

1
ответ дан 1 December 2019 в 13:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: