потокобезопасный с поп и посетить в C ++ очереди - PullRequest
3 голосов
/ 17 февраля 2020

Я знаю, что std :: queue не является потокобезопасным, но я не хочу блокировать очередь. поэтому я использую pop & pu sh limit для использования.

например, когда я хочу pop: у меня есть перечисление express первый элемент статуса

enum {
    Busy = 1,
    Unused,
}

, когда я добавить элемент в очередь:

void UserAdd() {
    lock.lock();
    element.status = BUSY;
    queue.push_back(element);
    lock.unlock();
}

при посещении:

//only visit function, and every element only called once.
void UserVisit() {
    auto header = queue.front();
    .......
    queue.front().status = UNUSED;
    return ;
}

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

Если первый элемент Занят, подождите;

Если первый элемент не используется, выведите:

void UserPop() {
    while (queue.front().status != Unused) {
        usleep(200);
    }
    lock.lock();
    queue.pop();
    lock.unlock();
}

поток A: 1. UserAdd, 2. UserVisit, 1.UserAdd, 2.UserVisit l oop. ..

поток B: 1. UserPop.

Безопасен ли поток UserPop () && UserVisit?.

Я думаю, что это безопасно для потока.

1 Ответ

3 голосов
/ 17 февраля 2020

Нет безопасности потока

Обратите внимание, что функция-член pop() изменяет очередь. Если UserPop() вызывается несколькими потоками одновременно, один поток может изменить очередь, вызвав для нее pop(), в то время как другой поток читает очередь, вызвав front():

void UserPop() {
    while (queue.front().status != Unused) { // <-- read queue
        usleep(200);
    }
    queue.pop(); // <-- modify queue
}

, так как сама очередь, std::queue, не обрабатывает параллельный доступ для вас, UserPop() не является поточно-ориентированным.


Сделайте его поточно-безопасным

Простой способ сделать Потокобезопасным является добавление мьютекса и удержание блокировки при чтении или изменении очереди:

std::mutex mtx;

// ...

void UserPop() {
    std::unique_lock<std::mutex> lck(mtx);
    // mtx is locked at this point
    while (queue.front().status != Unused) {
        lck.unlock();
        usleep(200); // mtx is not locked
        lck.lock();
    }
    // mtx is locked at this point
    queue.pop();
}

std::queue функции-члены front() и pop() выше всегда вызываются в то время как удержание блокировки на мьютексе.

Однако вы можете рассмотреть возможность использования std::condition_variable. Это обеспечивает оптимизацию по занятому ожиданию:

std::mutex mtx;
std::condition_variable cv;

void UserPop() {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck, [this]() { return queue.front().status == Unused; });
    queue.pop();
}
...