为什么std :: queue不是线程安全的? [英] Why is std::queue not thread-safe?

查看:999
本文介绍了为什么std :: queue不是线程安全的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

主题说明了这一点.我不明白为什么在没有像其他数据结构一样涉及迭代器的情况下,为什么std :: queue(或一般而言:任何队列)在本质上不是线程安全的.

The topic says it. I don't understand why the std::queue (or in general: any queue) is not thread-safe by its nature, when there is no iterator involved as with other datastructures.

按照通用规则

  • 至少有一个线程正在写入...
  • 另一个线程正在从共享资源中读取

我应该在以下示例代码中发生冲突:

I should have gotten a conflict in the following example code:

#include "stdafx.h"
#include <queue>
#include <thread>
#include <iostream>

struct response
{
    static int & getCount()
    {
        static int theCount = 0;
        return theCount;
    }

    int id;
};


std::queue<response> queue;

// generate 100 response objects and push them into the queue
void produce()
{
    for (int i = 0; i < 100; i++)
    {
        response r; 
        r.id = response::getCount()++;
        queue.push(r);
        std::cout << "produced: " << r.id << std::endl;
    }
}

// get the 100 first responses from the queue
void consume()
{
    int consumedCounter = 0;
    for (;;)
    {       
        if (!queue.empty())
        {
            std::cout << "consumed: " << queue.front().id << std::endl;
            queue.pop();
            consumedCounter++;
        }

        if (consumedCounter == 100)
            break;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{

    std::thread t1(produce);
    std::thread t2(consume);

    t1.join();
    t2.join();

    return 0;
}

一切似乎都正常: -不会违反完整性/不会破坏数据 -消费者获取元素的顺序正确(0 <1 <2 <3 <4 ...),当然产品的顺序也正确.和缺点.打印是随机的,因为不涉及信号传递.

Everything seems to be working fine: - No integrity violated / data corrupted - The order of the elements in which the consumer gets them are correct (0<1<2<3<4...), of course the order in which the prod. and cons. are printing is random as there is no signaling involved.

推荐答案

假设您检查!queue.empty(),输入下一个块,然后在访问queue.first()之前,另一个线程将删除(弹出)唯一的元素,因此您查询的是空队列.

Imagine you check for !queue.empty(), enter the next block and before getting to access queue.first(), another thread would remove (pop) the one and only element, so you query an empty queue.

使用如下所示的同步队列

Using a synchronized queue like the following

#pragma once

#include <queue>
#include <mutex>
#include <condition_variable>

    template <typename T>
    class SharedQueue
    {
    public:
        SharedQueue();
        ~SharedQueue();

        T& front();
        void pop_front();

        void push_back(const T& item);
        void push_back(T&& item);

        int size();
        bool empty();

    private:
        std::deque<T> queue_;
        std::mutex mutex_;
        std::condition_variable cond_;
    }; 

    template <typename T>
    SharedQueue<T>::SharedQueue(){}

    template <typename T>
    SharedQueue<T>::~SharedQueue(){}

    template <typename T>
    T& SharedQueue<T>::front()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        while (queue_.empty())
        {
            cond_.wait(mlock);
        }
        return queue_.front();
    }

    template <typename T>
    void SharedQueue<T>::pop_front()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        while (queue_.empty())
        {
            cond_.wait(mlock);
        }
        queue_.pop_front();
    }     

    template <typename T>
    void SharedQueue<T>::push_back(const T& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.push_back(item);
        mlock.unlock();     // unlock before notificiation to minimize mutex con
        cond_.notify_one(); // notify one waiting thread

    }

    template <typename T>
    void SharedQueue<T>::push_back(T&& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.push_back(std::move(item));
        mlock.unlock();     // unlock before notificiation to minimize mutex con
        cond_.notify_one(); // notify one waiting thread

    }

    template <typename T>
    int SharedQueue<T>::size()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        int size = queue_.size();
        mlock.unlock();
        return size;
    }

front()的调用一直等到它具有一个元素并锁定基础队列,以便一次只能有一个线程访问它.

The call to front() waits until it has an element and locks the underlying queue so only one thread may access it at a time.

这篇关于为什么std :: queue不是线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆