это правильное использование переменных условия повышения? - PullRequest
4 голосов
/ 29 января 2010

Я написал следующую программу для альтернативного увеличения и удвоения счетчика (сначала увеличить) с помощью переменных условия повышения Может кто-нибудь сказать мне, если это правильное использование переменных условия повышения. Это работает правильно. Я не понимаю использование блокировки в ожидании вызова функции. Что означает condition.wait (блокировка)? Например, что такое использование двух замков с областью действия в приращении и умножении в этой программе. Как я могу избежать их?

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

int counter=0;
boost::mutex m1,m2;
bool incremented=false,multiplied=false;
boost::condition_variable c1,c2;
void Increment()
{
    {
        boost::mutex::scoped_lock lk(m1);
        counter++;
        incremented = true;
        c1.notify_one();

        while(!multiplied)
            c2.wait(lk);
        multiplied=false;

    }   
}
void Multiply()
{
    {
        boost::mutex::scoped_lock lk(m2);
        while(!incremented)
            c1.wait(lk);
        incremented = false;
        counter = counter*2 ;
        multiplied = true;
        c2.notify_one();
    }
}

void IncrementNtimes(int n){

    for(int i=0;i<n;i++){
        Increment();
    }
}

void MultiplyNtimes(int n){

    for(int i=0;i<n;i++){
        Multiply();
    }
}
int main(int argc, char* argv[])
{
    srand ( time(NULL) );

    boost::thread thrd1(boost::bind(&IncrementNtimes,20));
    boost::thread thrd2(boost::bind(&MultiplyNtimes,20));
    thrd1.join();
    thrd2.join();
    cout<<"Main counter is:"<<counter<<endl;
    return 0;
}

Ответы [ 3 ]

18 голосов
/ 29 января 2010

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

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

Вы "держите" мьютекс, блокируя его. В этом смысл замков, и поэтому вы не можете их «избежать». Весь смысл блокируемых областей состоит в том, что, если у вас есть только один мьютекс m, когда один из потоков удерживает блокировку на m, другой поток гарантированно не будет удерживать блокировку на m. Если вы правильно закодировали, удержание блокировки на m должно быть предварительным условием для доступа к counter, и поэтому значение counter должно быть предсказуемым.

Теперь по поводу wait(). Вызов wait() означает «Я снимаю блокировку этого мьютекса, пока кто-то другой не сигнализирует об этом условии, и затем я хочу вернуть его». Тем временем поток останавливается. Таким образом, при условии, что у вас есть только один мьютекс m и условие c, а lk - это блокировка m, строка c.wait(lk) означает, что поток откажется от блокировки lk при m и затем приостановите выполнение, пока какой-нибудь другой поток не вызовет c.notify_one() (или c.notify_all()). Когда ожидающий поток вернется из вызова на wait(), он автоматически повторно получит блокировку lk на m и, таким образом, снова получит доступ к counter.

Наконец, эти повышающие блокировки являются «ограниченными». Это означает, что они освобождаются автоматически при уничтожении (когда они выходят за рамки). Таким образом, в этом случае каждая функция удерживает свою блокировку до тех пор, пока она не выйдет, за исключением случаев, когда она отдает свою блокировку для ожидания и приостанавливает выполнение в ожидании сигнала.

4 голосов
/ 29 января 2010

Блокировки привязаны к условным переменным, так как условные переменные не имеют состояния - если поток A сигнализирует об отсутствии официантов, а поток B затем входит в ожидание, B не будет разбужен. По этой причине должно быть состояние, которое также связано с переменной условия (в данном случае, увеличено и умножено). Блокировка защищает это состояние, доступ к которому осуществляется через несколько потоков. Когда вы передаете блокировку в wait (), wait атомарно снимает блокировку и ждет для условной переменной, а затем возвращает блокировку, когда ожидание возвращается. Это означает, что нет окна, в котором состояние за условной переменной могло бы измениться и произошло ожидание.

Например, если переменная условия не была привязана к блокировке:

// In thread A
while(!incremented) 

// Context switch to Thread B:
incremented = true;

// Context switch to Thread A:) 
   c1.wait();  // Whoops, waiting for a condition that already happened.
2 голосов
/ 29 января 2010

Переменная условия всегда должна быть связана с блокировкой. Суть в том, что вы должны удерживать блокировку для вызова wait (), но как только внутри wait () блокировка снимается. При подаче сигнала вы также должны удерживать блокировку, и, наконец, ожидание не вернется (даже после сигнала), пока сигнализатор не снимет блокировку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...