2013-02-27 3 views
0

Недавно я начал работать над одним унаследованным проектом и попытался исправить segfaults (double delete). Многие из них происходят на boost::shared_ptr деструкторе или operator= (на объектах, которые содержат shared_ptr). В коде содержится массовое использование shared_ptr-s, включая копирование, сброс(), назначение и т. Д. Согласно boost docs, у нас неверное использование - его небезопасно уничтожать/копировать/перезапускать тот же shared_ptr во многих потоках.boost :: shared_ptr drop-in replacement

Блокировка каждый раз кажется невозможным, поэтому im ищет замену для boost :: shared_ptr. Поэтому возникает вопрос: если я заменил все boost::shared_ptr на std::shared_ptr или std::tr1::shared_ptr, то разрешим эту проблему? Кажется, tr1 - более безопасная версия, но для меня это непонятно. Второй вопрос - версия C++ 0x лучше, чем tr1? (Обратите внимание, мы имеем GCC 4.4.6 и не может обновить его)

gcc docs Согласно наблюдениям, C++ 11 станд :: shared_ptr должен исправить это, но им не уверен gcc4.4 версии ...

UPD : Просто эксперимент цветовой и теперь я знаю, что все 3 реализаций этого сегментацию на этом коде (GCC 4.4) .. кажется, я должен сделать пользовательский класс или, возможно, другой обходным путем ...

#include <iostream> 
#include <boost/thread.hpp> 
#include <boost/shared_ptr.hpp> 

typedef boost::shared_ptr<int> ptrtype; 

ptrtype p(new int); 

void test() { 
     for(long i=0; i<1000000; ++i) { 
       ptrtype p1 = p; 
       p = ptrtype(); 
       p.reset(new int); 
     } 
} 

int main() { 
     boost::thread_group tg; 
     for(int i=0; i<100; ++i) tg.add_thread(new boost::thread(test)); 
     tg.join_all(); 
     std::cout << "Normal exit\n"; 
     return 0; 
} 
+3

boost :: shared_ptr довольно прочный. Я не думаю, что замена его другой реализацией собирается «решить» что угодно. Если вы видите двойные удаления, вы будете продолжать их видеть, пока не найдете и не исправите свои ошибки. – Bukes

+4

«Блокировка каждый раз кажется невозможной» означает, что у вас есть проблемы с потоками. Изменение реализации интеллектуального указателя не собирается вас покупать. boost :: shared_ptr не является ошибкой. –

+0

Вы ищете универсальный общий указатель? –

ответ

1

Шаг 1: Создайте класс, подобный этому, и замените его использованием boost::shared_ptr<T>.

template<typename T> 
struct trivial_ptr { 
    T* t; 
    template<typename U> 
    void reset(U* p) {t=p;} 
    void reset(T* p = NULL) { t=p; } 
    template<typename U> 
    trivial_ptr<T>& operator=(trivial_shared_ptr<U>const& o) { 
    t = o.t; 
    return *t; 
    } 
    explicit trivial_ptr(T* p):t(p) {} 
    ... 
}; 

Этот класс не предназначен для запуска, а просто для компиляции с правильным интерфейсом. После того, как вы скомпилировали, вы можете убедиться, что знаете, какие части интерфейса boost::shared_ptr вы используете. (Вы используете пользовательские удалители? И т. Д. - проблема может быть сложнее или проще, и выше может помочь ее протестировать)

Как только вы там, вы можете решить, как тяжело будет написать shared_ptr<T> который обрабатывает несколько потоков, обращаясь к одной и той же переменной одновременно.

Теперь это очень грязно. Если один поток reset s задан shared_ptr, а другой поток читает его, указатель, считанный с , может быть полностью недействительным к тому времени, когда поток чтения будет иметь к нему доступ. По сути, вам нужно защитить все доступ к основному указателю в мьютексе, что совершенно непрактично.

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

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

+0

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

+0

@PSIAlt: Вы не читали его последний абзац? Вы не можете исправить эту проблему, создав новый тип указателя. перейдя на mutex-wrap каждый экземпляр указателя, и даже тогда у вас все еще будут проблемы, потому что кто-то собирается предположить, что старый указатель был вокруг. Вы не можете избежать того факта, что вам передали ужасно глючную кодовую базу, и вы нужно исправить это. –

+0

@NicolBolas ugh, похоже, нет другого пути, чем .. dunno what yet .. Похоже, у меня нет шансов переписать это, не бросив что-то. Но, спасибо за указание на блокировку – PSIAlt

0

std::shared_ptrможет использовать атомный Интс если компилятор/архитектура/реализация поддерживает/использует его. Но я бы не стал делать ставку на это, и это сделает ваш код менее портативным. Но это может работать как временное решение (например, иметь запущенную программу, чтобы вы понимали, что должен делать код).

Возможно, вам может понадобиться использовать обертку shared_ptr, но ваш код по-прежнему должен быть проверен на наличие блокировок.

+3

' std :: shared_ptr' должен быть потокобезопасным ... но * только * как поточно-безопасный как 'boost: : shared_ptr'. То есть, два разных объекта shared_ptr могут ссылаться на одни и те же данные, и удаление будет прекрасным. Но вы не можете ткнуть в * тот же * shared_ptr объект из двух разных потоков. Последнее - проблема, которую имеет ОП. –

+0

'shared_ptr' ** требуется ** для потокобезопасности, но только для копирования экземпляров одновременно, не изменяя их одновременно –

1

Проблема, с которой вы столкнулись, заключается в попытке изменить один и тот же экземпляр переменной в двух отдельных потоках (AKA - гонка данных). shared_ptr больше не защищен от этого, чем int. Ваша ссылка на документы gcc говорит то же самое («тот же уровень безопасности потоков, что и встроенные типы»). Попытка изменить один и тот же экземпляр shared_ptr в двух разных потоках требует некоторой синхронизации для предотвращения гонки данных. Попытка изменить два разных экземпляра shared_ptr, которые указывают на один и тот же объект, в порядке (никакая гонка данных или shared_ptr не должна выполнять все, что необходимо для предотвращения гонки данных внутри). Попытка изменить объект, на который они указывают, - это также гонка данных.

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