C ++ Threading, дубликаты / отсутствующие темы - PullRequest
0 голосов
/ 06 октября 2018

Я пытаюсь написать программу, которая одновременно добавляет и удаляет элементы из «хранилища».У меня есть класс «Monitor», который обрабатывает операции «storehouse»:

class Monitor
{
private:
    mutex m;
    condition_variable cv;
    vector<Storage> S;
    int counter = 0;
    bool busy = false;;
public:

    void add(Computer c, int index) {
        unique_lock <mutex> lock(m);
        if (busy)
            cout << "Thread " << index << ": waiting for !busy " << endl;
        cv.wait(lock, [&] { return !busy; });
        busy = true;
        cout << "Thread " << index << ": Request: add " << c.CPUFrequency << endl;

        for (int i = 0; i < counter; i++) {
            if (S[i].f == c.CPUFrequency) {
                S[i].n++;
                busy = false;   cv.notify_one();
                return;
            }
        }
        Storage s;
        s.f = c.CPUFrequency;
        s.n = 1;
        // put the new item in a sorted position
        S.push_back(s);
        counter++;
        busy = false; cv.notify_one();
    }
}

Потоки создаются следующим образом:

void doThreadStuff(vector<Computer> P, vector <Storage> R, Monitor &S)
{
    int Pcount = P.size();

    vector<thread> myThreads;
    myThreads.reserve(Pcount);

    for (atomic<size_t> i = 0; i < Pcount; i++)
    {
        int index = i;
        Computer c = P[index];
        myThreads.emplace_back([&] { S.add(c, index); });
    }

    for (size_t i = 0; i < Pcount; i++)
    {
        myThreads[i].join();
    }
// printing results
}

Запуск программы дал следующие результаты:

enter image description here

Я знаком с условиями гонки, но для меня это не похоже.Моя ставка была бы на что-то, относящееся к ссылке, потому что в результатах мы можем видеть, что для каждого «отсутствующего потока» (темы 1, 3, 10, 25) я получаю «дублирующие темы» (темы 2, 9, 24, 28).

Я пытался создать локальные переменные в функциях и циклах, но это ничего не изменило.

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

Я использую Visual Studio 2017

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Другой подход заключается в передаче S, i и c в качестве аргументов вместо их захвата путем определения следующих не захватывающих лямбда, th_func:

auto th_func = [](Monitor &S, int index, Computer c){ S.add(c, index); };

Таким образом, вы должны явно обернуть аргументы, которые должны быть переданы по ссылке , в вызываемый объект потока с помощью std::reference_wrapper с помощьюшаблон функции std::ref().В вашем случае только S:

for (atomic<size_t> i = 0; i < Pcount; i++) {
    int index = i;
    Computer c = P[index];
    myThreads.emplace_back(th_func, std::ref(S), index, c);
}

Если не обернуть std::reference_wrapper аргументы, которые должны быть переданы по ссылке, приведет к ошибке времени компиляции.То есть следующее не будет компилироваться:

myThreads.emplace_back(th_func, S, index, c); // <-- it should be std::ref(S)

См. Также этот вопрос .

0 голосов
/ 06 октября 2018

Здесь вы ловите локальные переменные по ссылке в цикле, они будут уничтожаться при каждом повороте, вызывая неопределенное поведение:

for (atomic<size_t> i = 0; i < Pcount; i++)
{
    int index = i;
    Computer c = P[index];
    myThreads.emplace_back([&] { S.add(c, index); });
}

Вы должны поймать index и c по значению:

myThreads.emplace_back([&S, index, c] { S.add(c, index); });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...