Как правильно использовать мьютекс в качестве параметра для функции-члена в потоке? - PullRequest
0 голосов
/ 16 апреля 2019

Моя проблема в том, что я не знаю, как правильно использовать мьютекс.Я понимаю, как это работает теоретически, но я не знаю, почему это не работает в моем коде. Я подумал, что если я использую мьютекс в var, он будет заблокирован, пока не будет разблокирован.Тем не менее кажется, что у меня все еще есть гонка данных.

Я попытался определить мьютекс класса и мьютекс в основном, который я передаю по ссылке.Каким-то образом ничего из этого не работает.


class test {
public:
    void dosmth(std::mutex &a);
    int getT(){return t;};

private:
    int t = 0;
};


void test::dosmth(std::mutex &a) {
    for(;;){
        a.lock();
        t++;
        if(t==1000){
            t=0;
        }
        a.unlock();
    }
}

int main() {
    test te;
    std::mutex help;
    std::thread t(&test::dosmth, std::addressof(te), std::ref(help));
    for(;;){
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
    }
}

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

Ответы [ 3 ]

2 голосов
/ 16 апреля 2019

Как уже упоминал Майкл, вы должны синхронизировать читателя и писателя, чтобы избежать неопределенного поведения.Вместо передачи mutex в качестве аргумента, общая схема состоит в том, чтобы сделать mutex членом объекта (te), блокировать и разблокировать каждый раз (предпочитайте lock_gaurd вместо ручной блокировки и разблокировки)введите функцию-член (которая изменяет внутреннее состояние объекта).Вот некоторый псевдокод:

class Foo{
    std::mutex m; // reader and writer are sync'ed on the same mutex
    int data_to_sync;
public:
    int read(){
        lock_gaurd<mutex> lg(m); //RAII lock
        return data_to_sync;
        //automagically released upon exit
    }
    void write(){
        lock_gaurd<mutex> lg(m);
        data_to_sync++;
    }
};
1 голос
/ 16 апреля 2019

Мьютекс может гарантировать взаимное исключение только в том случае, если указанный мьютекс используется для регулирования входа в все критические секции кода, где к данному объекту будет осуществляться одновременный доступ.В вашем случае ваш второй поток изменяет значение объекта te.t, пока ваш основной поток читает значение того же объекта.Однако только один поток использует мьютекс для защиты доступа к te.t.Объект te.t не является атомарным.Следовательно, у вас есть гонка данных и, следовательно, неопределенное поведение [intro.races] / 21 .

Вы также должны блокировать и разблокировать мьютекс в цикле for(;;) в mainНапример:

    for(;;){
        help.lock();
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
        help.unlock();
    }

или лучше, используя std :: lock_guard :

    for(;;){
        std::lock_guard lock(help);
        for (int i = 0; i <te.getT() ; ++i) {
            std::cout<<te.getT()<<std::endl;
        }
    }
0 голосов
/ 16 апреля 2019

Я подумал, что если я использую мьютекс в переменной ...

Вы не используете мьютекс "на переменную". Блокировка мьютекса не позволяет другим потокам одновременно блокировать тот же мьютекс, но не не позволяет другим потокам получать доступ к какой-либо конкретной переменной (переменным).

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

...