Является ли mutex + атомарным, чтобы сделать этот поток кода безопасным, или достаточно мьютекса? - PullRequest
1 голос
/ 30 июня 2011

У меня есть некоторые сомнения, достаточно ли мьютексов для обеспечения безопасности потоков в следующем примере кода или если требуется атомарность. Короче говоря, вопрос: сделает ли idxActive обычный int этот поток кода небезопасным? Или код даже с атомарным потоком небезопасен? :( Если это важно, я использую 32-разрядную версию x86, linux, gcc 4.6. Конечно, я предполагаю, что 32- или 64-битные не делают различий, но если есть разница между 32 и 64-битными, я бы хотел знать.

#include <memory>
#include <boost/thread/thread.hpp>
#include <string>
#include <vector>
#include <atomic>
#include <boost/thread/mutex.hpp>
using namespace std;
using namespace boost;
static const int N_DATA=2;
class Logger
{
    vector<string> data[N_DATA];
    atomic<int> idxActive;
    mutex addMutex;
    mutex printMutex;
public:
    Logger()
    {
        idxActive=0;
        for (auto& elem: data)
            elem.reserve(1024);
    }
private:
    void switchDataUsed()
    {
        mutex::scoped_lock sl(addMutex);
        idxActive.store( (idxActive.load()+1)%N_DATA );
    }
public:
    void addLog(const string& str)
    {
        mutex::scoped_lock sl(addMutex);
        data[idxActive.load()].push_back(str);
    }
    void printCurrent()
    {
        mutex::scoped_lock sl(printMutex);
        switchDataUsed();
        auto idxOld=(idxActive.load()+N_DATA-1)%N_DATA; //modulo -1
        for (auto& elem:data[idxOld])
            cout<<elem<<endl;
        data[idxOld].clear();
    }
};
int main()
{
    Logger log;
    log.addLog(string("Hi"));
    log.addLog(string("world"));
    log.printCurrent();
    log.addLog(string("Hi"));
    log.addLog(string("again"));
    log.printCurrent();
    return 0;
}

Ответы [ 2 ]

3 голосов
/ 30 июня 2011

Вам не нужно использовать атомарные переменные, если все обращения к этим переменным защищены мьютексом.Это относится к вашему коду, так как все открытые функции-члены блокируют addMutex при входе.Поэтому addIndex может быть простым int, и все будет работать нормально.Блокировка и разблокировка мьютекса гарантируют, что правильные значения станут видимыми для других потоков в правильном порядке.

std::atomic<> разрешает одновременный доступ вне защиты мьютекса, гарантируя, что потоки видят правильныезначения переменной, даже перед лицом одновременных изменений.Если вы придерживаетесь порядка памяти по умолчанию, это также гарантирует, что каждый поток читает значение переменной latest .std::atomic<> может использоваться для написания поточно-безопасных алгоритмов без мьютексов, но не требуется, если все обращения защищены одним и тем же мьютексом . 1013 *.

Важное обновление :

Я только что заметил, что вы используете два мьютекса: один для addLog и один для printCurrent.В этом случае вам нужно нужно idxActive, чтобы быть атомарным, потому что отдельные мьютексы не обеспечивают никакой синхронизации между ними .

1 голос
/ 30 июня 2011

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

В вашем случае код должен быть безопасным. Только 1 поток за один раз может ввести printCurrent(). Во время выполнения этой функции другие потоки могут вызывать addLog() (но одновременно только 1). В зависимости от того, был ли switchCurrent уже выполнен, эти записи войдут в текущий журнал или нет, но ни одна не будет введена во время итерации по нему. Только 1 поток одновременно может ввести addLog, который разделяет свой мьютекс с switchCurrent, поэтому они не могут быть выполнены одновременно.

Это будет иметь место, даже если вы сделаете idxActive простым int Mh, модель памяти C ++ работает только с однопоточным кодом - поэтому я не слишком уверен, теоретически может ли он сломаться Это. Я думаю, что если вы сделаете idxActive энергозависимым (по сути, вообще не позволяющим оптимизировать загрузку / хранение в нем), это будет нормально для всех практических целей. В качестве альтернативы вы можете удалить мьютекс из switchCurrent, но тогда вам нужно оставить idxActive атомарным.

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

...