2015-03-14 2 views
3

Для примера рассмотримли подсчет ссылок поточно

class ProcessList { 
private 
    std::vector<std::shared_ptr<SomeObject>> list; 
    Mutex mutex; 
public: 
    void Add(std::shared_ptr<SomeObject> o) { 
     Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff 
     list.push_back(std::make_shared<SomeObject>(o). 
    } 

    void Remove(std::shared_ptr<SomeObject> o) { 
     Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff 
     // Code to remove said object but indirectly modifying the reference count in copy below 
    } 

    void Process() { 
     std::vector<std::shared_ptr<SomeObject>> copy; 

     { 
      Locker locker(&mutes); 
      copy = std::vector<std::shared_ptr<SomeObject>>(
       list.begin(), list.end() 
      ) 
     } 
     for (auto it = copy.begin(), it != copy.end(); ++it) { 
      it->Procss(); // This may take time/add/remove to the list 
     } 
    } 
}; 

Одна нитка Process. Несколько потоков запускают добавление/удаление.

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

+0

Подсчет ссылок может быть реализован поточно-безопасно. Учитывая, что у вас есть C++ 11, который также имеет потоки, я бы предположил, что refcounting также реализуется поточно-безопасным способом. BTW: Даже оригинальный 'shared_ptr' из Boost был уже потокобезопасным. –

+0

Имеет ли каждый счетчик ссылок мьютекс? –

+1

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

ответ

1

Да, стандарт (в §20.8.2.2, по крайней мере, от N3997), который должен требовать, чтобы подсчет ссылок был потокобезопасным.

Для ваших простых случаях, как Add:

void Add(std::shared_ptr<SomeObject> o) { 
    Locker locker(&mutex); 
    list.push_back(std::make_shared<SomeObject>(o). 
} 

... гарантии в стандарте достаточно сильны, что вы не должны мьютекс, так что вы можете просто:

void Add(std::shared_ptr<SomeObject> o) { 
    list.push_back(std::make_shared<SomeObject>(o). 
} 

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

{ 
     Locker locker(&mutes); 
     copy = std::vector<std::shared_ptr<SomeObject>>(
      list.begin(), list.end() 
     ) 
    } 

Это осуществляет полную копию как атомарные операции - ничто иное может изменить список во время копирования. Это гарантирует, что ваша копия дает вам снимок списка точно так же, как и при копировании. Если вы исключите мьютекс, подсчет ссылок будет по-прежнему работать, , но ваша копия может отражать изменения, сделанные во время копирования.

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

С вашего list на самом деле vector, вы должны иметь возможность упростить код копирования немного до copy = list.

Также обратите внимание, что ваш Locker является подмножеством того, что предлагает std::lock_guard.Полагаете, вы можете использовать:

std::lock_guard<std::mutex> locker(&mutes); 

... на его месте довольно легко.

+0

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

+0

@EdHeal: Нет ничего, что могло бы запретить реализацию вектора для обеспечения безопасности потоков, но это тоже не требуется. Если несколько потоков изменяют вектор, то да, вам нужно использовать мьютекс (или что-то еще) для сериализации этого доступа. –

+0

Это вектор общих указателей. - так что предметы безопасны, но вектор не является - это правильно –

-2

Если ваша архитектура процессора имеет атомный прирост/декремент, и вы использовали это для подсчета ссылок, то нет, это небезопасно; C++ не гарантирует безопасность потока операций x ++/x-- на любом из его стандартных типов.

Используйте atomic<int>, если ваш компилятор поддерживает их (C++ 11), иначе вам понадобится блокировка.

Дальнейшие ссылки:

+2

Вопрос заключается в том, является ли 'std :: shared_ptr' (который является тем, что используется в коде, предоставленном OP) является потокобезопасным. Здесь нет операции «x ++/x--» (если только в реализации shared_ptr). – davmac

0

Это будет над головой, чтобы иметь взаимную блокировку для подсчета ссылок.

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

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