繁体   English   中英

固定大小的线程安全队列

[英]Thread safe queue with fixed size

我想要做的是将整数推送到具有多个线程的threadSafe队列实现中,并与另一系列的线程并发弹出插入的数字。 所有这些操作都必须是线程安全的,但是我想拥有的另一个选择是,队列的大小必须固定,就像缓冲区一样。 如果缓冲区已满,则所有推入线程都必须等待弹出线程释放一些插槽。

这是我对队列/缓冲区的实现,它似乎可以工作,但经过几次迭代后,它停止并保持被阻塞而没有任何错误。

#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>

template <typename T>

class Queue
{
private:
    std::queue<T> queue_;
    std::mutex mutex_;
    std::condition_variable cond_;

public:

    T pop()
    {
        std::unique_lock<std::mutex> mlock(mutex_);

        cond_.wait(mlock, [this]{return !queue_.empty();});

        auto val = queue_.front();
        queue_.pop();
        return val;
    }

    void pop(T& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);

        cond_.wait(mlock, [this]{return !queue_.empty();});

        item = queue_.front();
        queue_.pop();
    }

    void push(const T& item, int buffer)
    {
        std::unique_lock<std::mutex> mlock(mutex_);

        while (queue_.size() >= buffer)
        {
            cond_.wait(mlock);
        }

        queue_.push(item);
        mlock.unlock();
        cond_.notify_one();
    }

    Queue()=default;
    Queue(const Queue&) = delete;            // disable copying
    Queue& operator=(const Queue&) = delete; // disable assignment

};

缓冲区的大小是在push函数中使用可变缓冲区定义的。 这是用法示例:

 void prepare(Queue<int>& loaded, int buffer, int num_frames)
 {
     for (int i = 0; i < num_frames; i++)
     {
         cout<< "push "<<i<<endl;
         loaded.push(i, buffer);
     }
 }

 void load (vector<Frame>& movie, Queue<int>& loaded, int num_frames,
                            int num_points, int buffer, int height, int width)
    {
        for (int i = 0; i < num_frames; i++)
        {
            int num = loaded.pop();
            cout<< "pop "<<num<<endl;
    }
}

int main()
{
    srand(time(NULL));

    int num_threadsXstage = 4;

    int width = 500;
    int height = 500;

    int num_points = width * height;

    int num_frames = 100;

    int frames_thread = num_frames/num_threadsXstage;

    int preset = 3;

    int buffer = 10;

    //Vectors of threads
    vector<thread> loader;

    //Final vector
    vector<Frame> movie;
    movie.resize(num_frames);

    //Working queues
    Queue<int> loaded;

    //Prepare loading queue task
    thread preparator(prepare, ref(loaded), buffer, num_frames);

    for (int i = 0; i < num_threadsXstage; i++)
    {
        //stage 1
        loader.push_back(thread(&load, ref(movie), ref(loaded), frames_thread,
                                num_points, buffer, height, width));

    }


    // JOIN
    preparator.join();

    join_all(loader);

    return 0;
}

您的pop函数可以允许线程等待push以前进,但是它们不调用任何notify函数。 您必须在任何时候使条件变量上阻塞的线程向前推进时都必须调用适当的notify函数。

尽管解释原因非常复杂,但是您应该在保持锁的状态下调用notify_all或调用notify_one 从理论上讲,可以“唤醒错误的线程”,否则,因为您对两个谓词使用了相同的条件变量(队列不为空且队列未满)。

为避免很难理解的故障模式,请始终执行以下三件事之一:

  1. 不要使用相同的条件变量来处理多个谓词。 例如,对“不为空”使用一个条件变量,对“不满”使用另一个条件变量;
  2. 始终使用notify_all ,从不使用notify_one 要么
  3. 始终在拿着互斥锁的同时调用通知功能。

只要您遵循这三个规则中的至少一个,就可以避免使用晦涩的失败模式,即在释放互斥锁后仅唤醒一个选择进入睡眠状态的线程,而剩下的唯一可以处理这种情况的线程仍然处于阻塞状态。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM