Почему вызов функции-члена в отдельном потоке приводит к недетерминированному поведению c? - PullRequest
1 голос
/ 12 февраля 2020

Я разбил свою проблему на что-то воспроизводимое. В следующем коде у нас есть класс printer. printer::run напечатает id_ этого printer. printer::spawn даст вам ветку с новым принтером.

В main Я создаю 10 принтеров. Созданные темы будут объединены в конце main. Я ожидал бы, что мы получим числа от 0 до 9 в некотором порядке каждый раз, когда мы запускаем это (это «детерминированное c поведение» из заголовка). Это не вариант. Иногда мы получаем другие цифры. Это почему?

#include <thread>
#include <iostream>
#include <vector>

class printer
{
public:
    printer(int i) : i_(i){};
    void run()
    {
        std::cout << i_ << '\n';
    }
    std::shared_ptr<std::thread> spawn()
    {
        return std::make_shared<std::thread>([=]() { run(); });
    }

private:
    int i_;
};

int main()
{
    std::vector<std::shared_ptr<std::thread>> vec;
    for (int i = 0; i < 10; ++i)
    {
        printer p{i};
        vec.push_back(p.spawn());
    }
    for (auto a : vec)
    {
        a->join();
    }
    return 0;
}

Некоторые мысли о моем ожидании:

Я бы догадался, что мы копируем состояние текущего printer при вызове spawn, потому что мы фиксируем состояние по значению (лямбда) не по ссылке.

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

И даже если этого не происходит, у каждого потока есть свой printer. Что мне не хватает?

Некоторые примеры выходных данных:

(для краткости я положил все числа в одну строку)

1, 3, 4, 2, 5, 6, 7, 8, 9, -116618192
1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144
1, 2, 33, , 6, 6, 7, 9, 9, 1344437296
1, 43, 2, 6, , 6, 7, 8, 9, -1194894256

Некоторые выходы не имеют всех 10 число ...

Ответы [ 2 ]

2 голосов
/ 12 февраля 2020

Здесь

{
    printer p{i};
    vec.push_back(p.spawn());
}

Объект p имеет время жизни, которое заканчивается на }. Следовательно, лямбда, которую вы, pu sh в векторе, попытаетесь вызвать функцией-членом объекта, которого уже нет в живых. Ваш код имеет неопределенный бехворор, и видеть правильные результаты - только совпадение.

1 голос
/ 12 февраля 2020

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

По сути, все ваши потоки будут использовать один и тот же printer объект. В какой-то момент занимаемая им память перезаписывается, когда она используется для хранения других данных.

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

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