Mutex как член класса - PullRequest
       7

Mutex как член класса

0 голосов
/ 24 августа 2018

Я пытаюсь создать простой шаблон подписчик-производитель, в котором несколько подписчиков и один производитель работают в разных потоках (причина в том, чтобы узнать больше о многопоточности).Однако я изо всех сил пытаюсь сделать мьютекс членом класса продюсеров.Код указан ниже:

class Producer {
private:
    vector<Subscriber* > subs;
    thread th;
    int counter;
    mutex mux;

public:
    Producer() : counter(counter), th(&Producer::run, this) {};
    void addSubscriber(Subscriber* s);
    void notify();
    void incrementCounter();
    void run();
    void callJoin(){th.join(); } 
};

void Producer::run() {
    for (int i = 0; i < 10; i++) {
        incrementCounter();
        this_thread::sleep_for(std::chrono::milliseconds(3000));
    }
}

void Producer::addSubscriber(Subscriber* s) {
    lock_guard<mutex> lock(mux);
    subs.push_back(s); 
}

void Producer::notify() {
    lock_guard<mutex> lock(mux);
    for (auto it = subs.begin(); it != subs.end(); ++it) {
        (*it)->setCounterCopy(counter);
    }
}

void Producer::incrementCounter() {
    counter++;
    notify(); 
}

Класс абонента:

class Subscriber {
private:
    string name;
    thread th;
    atomic<int> counterCopy = 0;

public:

    Subscriber(string name) : name(name),  th(&Subscriber::run, this) {};
    void run() {
        while (true) {
            cout << name << ": " << counterCopy << endl; 
            this_thread::sleep_for(std::chrono::milliseconds(1000));            
        }
    }
    void callJoin() { th.join(); }
    void setCounterCopy(int counterCopy) { this->counterCopy = counterCopy; };
};

На основном:

int main() {
    Producer p;
    Subscriber s1("Sub1");
    p.addSubscriber(&s1);
    s1.callJoin();
    p.callJoin();
    return 0;
}

Цель lock_guard состоит в том, чтобы предотвратитьпроизводитель уведомляет подписчиков в векторе одновременно с добавлением подписчика в вектор.Однако это исключение добавлено в lock_guard of notify Exception thrown at 0x59963734 (msvcp140d.dll) in Project1.exe: 0xC0000005: Access violation reading location 0x00000000. Кто-нибудь знает, что может быть причиной этого исключения?Если мьютекс установлен как глобальный параметр, это работает нормально.Не стесняйтесь комментировать и другие проблемы кода.Threading это все новое для меня.

Ответы [ 2 ]

0 голосов
/ 24 августа 2018

Я просто хочу добавить это к данному ответу @Sneftel для справки:

Из стандарта CPP (N4713) соответствующая часть выделена:

15.6.2 Инициализация баз и членов [class.base.init]

...

13 В конструкторе без делегирования инициализация выполняется вследующий порядок:
(13.1) - Во-первых, и только для конструктора самого производного класса (6.6.2), виртуальные базовые классы инициализируются в том порядке, в котором они отображаются при обходе слева направо на глубинунаправленный ациклический граф базовых классов, где «слева направо» - порядок появления базовых классов в списке базовых спецификаторов производного класса.
(13.2) - Затем прямые базовые классы инициализируются впорядок объявления в том виде, в каком они указаны в списке базовых спецификаторов (независимо от порядка инициализаторов mem).
(13.3) - Затем элементы нестатических данных инициализируются в порядке, в котором они были объявленыопределение класса (опять-таки независимо от порядка инициализации mem).
(13.4) - Наконец, составной оператор тела конструктора выполняется.
[Примечание.убедитесь, что базовые и членские подобъекты уничтожены в обратном порядке инициализации.—Конечная записка]

0 голосов
/ 24 августа 2018

Так что здесь происходит дурацкий порядок инициализации.

Члены класса создаются в том порядке, в котором они объявлены в классе.В вашем случае это означает сначала вектор подписчиков, затем поток, затем счетчик (!) И, наконец, мьютекс.Порядок, в котором вы указываете инициализаторы в конструкторе, не имеет значения.

Но!Создание объекта потока влечет за собой запуск потока, выполнение его функции.И это, в конце концов, приводит к использованию мьютекса, возможно, до того, как конструктор Producer достигнет точки, где он фактически его инициализирует.Таким образом, вы заканчиваете тем, что используете еще не сконструированный мьютекс, и (не то, что это является причиной вашей проблемы), а также еще не инициализированный счетчик.

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

В вашем случае достаточно просто переместить элементы мьютекса и счетчика до того, как член потока будет достаточным.

...