2011-12-17 4 views
2

У меня есть класс, представляющий различные виды инструментов (GCC, LEX, YACC, ...). Каждому экземпляру присваивается тип, представляющий инструмент, и разрешается специальная конфигурация.Правильный способ реализации поточно-безопасного метода в C++ 11

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

int Tool::addType(std::string typeName, 
     std::string typeFlagName) 
    throw (GP::Exceptions::LockAcquisitionException) 
{ 
    static std::timed_mutex mutex; 
    std::unique_lock<std::timed_mutex> lock{mutex}; 

    int typeId = 0; 

    if (lock.try_lock_for(std::chrono::microseconds(100))) 
    { 
     int typeId = typeNames.size(); 
     typeNames[typeId] = typeName; 
     typeFlagNames[typeId] = typeFlagName; 
    } 
    else 
    { 
     throw GP::Exceptions::LockAcquisitionException{"Unable to generate new type ID within 100 microseconds."}; 
    } 
    return typeId; 
} 

Я хотел бы знать, если это хорошее решение, или если Я что-то упускаю. Если это нормально, есть ли другое решение, менее подробное?

+4

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

+1

Я согласен с руахом. У вас должен быть только элемент std :: mutex для всего объекта и каждого вызова участника. Затем просто используйте простой std :: lock_guard вместо того, чтобы обманывать таймер. – Lalaland

+1

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

ответ

4

Почему-то не так?

class tool 
{ 
    std::atomic<int> index_; 
    std::array<std::pair<std::string, std::string>, 2048> types_; // Should be more than enough room. 

    int addType(std::string typeName, std::string typeFlagName) 
    { 
     int id = index++; 

     if(id >= types_.size()) 
      throw GP::Exceptions{"To many types."}; 

     types_[id] = std::make_pair(typeName, typeFlagName); 

     return id; 
    } 

}; 

Вы могли бы сделать это немного умнее с помощью зЬй :: вектор и иметь блокировку только тогда, когда он должен быть перераспределены для большего размера.

ПРИМЕЧАНИЕ: предложение throw() устарело в C++ 11.

+0

«Вы можете сделать это немного умнее, используя std :: vector и заблокировать его только тогда, когда его нужно перераспределить для большего размера». - Я не вижу никакого способа сделать это, не блокируя его, прежде чем получить размер ... – jcoder

+0

@JohnB: И проблема в этом? – ronag

+0

Вы имеете в виду бросок в объявлении метода? Спасибо за ваш ответ, но я действительно хотел бы реализовать полную безопасность потоков, не используя обходное решение (хотя иногда это может быть лучше) – Geoffroy

3

Мое предложение было бы:

class tool 
{ 
    std::mutex myMutex; 

    std::vector<std::string> typeNames; 

    int addType(std::string typeName) 
    { 
     std::lock_guard myLock(myMutex); 
     typeNames.push_back(typeName); 
     return typeNames.size()-1; // 
    } 

    // Only hold lock when needed 
    void longComplexFunction(int f) 
    { 
     // Compute as much as possible before 
     int complexMagic = veryLongFunction(f); 

     { 
      std::lock_guard myLock(myMutex); 
      typeNames[complexMagic] += "s"; 
     } 
    } 

} 
+0

И что происходит, если lock_guard не может получить блокировку? – Geoffroy

+0

Он спит до тех пор, пока замок не будет доступен. – Lalaland

+0

только последний вопрос, что я могу сделать для геттеров? Есть что-то вроде boost :: shared_mutex, который я могу использовать вместе с std :: lock_guard? – Geoffroy

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