2012-02-14 3 views
3

Я пытаюсь понять, как использовать мьютексы с объектами в C++. У меня есть следующий (тривиальный) многопоточный код, я использую в качестве теста скорости:мьютексы с объектами

struct Rope{ 
    int n, steps, offset; 
    //std::mutex mut; 

    Rope() {} 
    Rope(int n, int steps, int offset) : n(n), steps(steps), offset(offset) {} 

    void compute(){ 
    double a[n]; 
    for (int i=0; i<n; i++) 
     a[i] = i + offset; 
    for (int step=0; step<steps; step++) 
     for (int i=0; i<n; i++) 
    a[i] = sin(a[i]); 
    } 

}; 

void runTest(){ 
    int numRuns = 30; 
    int n = 10000; 
    int steps = 10000; 

    std::vector<Rope> ropes; 
    std::vector<std::thread> threads; 
    for (int i=0; i<numRuns; i++) 
    ropes.push_back(Rope(n, steps, i)); 
    for (auto& r : ropes) 
    threads.push_back(std::thread(&Rope::compute, r)); 
    for (std::thread& t : threads) 
    t.join(); 
}  

код работает отлично, как есть, и видит ускорение ~ 4x на моей 4 ядре машины. Я, конечно, ничего не храню в веревке, поэтому нет необходимости в мьютексе. Если я теперь предполагаю, что у меня есть некоторые данные, которые мне нужны для защиты, я хотел бы добавить мьютекс к канату и (например,) вызов std :: lock_guard в цикле compute(). Однако, если я раскомментирую мьютексы, я получаю кучу ошибок компилятора об «использовании удаленной функции» для операторов присваивания и копирования. Что мне не хватает в моей цели - безопасно заблокировать объект?

ответ

6

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

class cMyClass { 
    boost::mutex myMutex; 
    cSomeClass A; 
public: 
    cSomeClass getA() { 
    boost::mutex::scoped_lock lock(myMutex); 
    return A; 
    } 
}; 

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

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

class cMyClass { 
    static boost::mutex myMutex; 
    cSomeClass A; 
public: 
    cSomeClass getA() { 
    boost::mutex::scoped_lock lock(myMutex); 
    return A; 
    } 
}; 

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

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

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

+0

@MikeSeymour Если я не понял, о чем он говорит, ему нужно использовать ** ту же ** мьютекс во всех потоках. Вставка его в контейнер с использованием семантики перемещения не будет работать. (С другой стороны, как это написано в настоящее время, каждый поток получает свой собственный объект «Rope», поэтому нет необходимости использовать мьютекс. Трудно сказать, какое правильное решение есть, когда проблема не была четко объяснена.) –

+0

@ Майк Сеймур: 'std :: mutex' не движется. Можно сделать «Rope» подвижным или скопированным по умолчанию, создавая 'std :: mutex' в каждом конструкторе' Rope'. Или, сделав мьютекс статичным, как предлагает Джеймс. Какой из этих двух подходов подходит, это то, что знает только ОП. –

+0

Моя ошибка; Я должен был проверить свои предположения перед публикацией. –

0

Вам не хватает факта, что mutex не подлежит копированию. Что это значит , чтобы сделать копию мьютекса? Место для приобретения и выпуска мьютекса находится в Rope::compute, и поскольку все потоки должны получить доступ к одному и тому же мьютексу , вы должны определить его в runTest и передать его ссылкой или указателем.

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