等待逐步生成的向量的片段

时间:2013-01-07 19:47:16

标签: c++ concurrency

我想在C ++中实现以下内容,但我不知道哪种是更简单的管理并发方式。

我有一个线程Producer,它将元素添加到向量V的背面。添加元素后,它将被视为只读。为了简单起见,我们假设我可以使用一个不会在迭代时使迭代器无效的向量,或者我将使用读写互斥锁来处理锁​​定。但是向量的读者有一个问题:他们可能想要访问V的多个连续元素,其中一些可能尚未生成。

在任何特定时刻,V都有一些元素,我将用“o”表示,并且Producer可能会添加更多元素,我将用“w”表示”。因此,V 概念中的数据如下所示:

o o o o w w w w

我强调“从概念上”,因为我不想把身体放在尚未生成的V元素/假人身上。现在,其中一位读者R对尚未完全生成的V段感兴趣:

o o o o w w w w
    | | | |
    ---R---

因此R需要等待V增长,直到它包含R想要的所有元素。在任何给定时刻,我都可以使用j生成的最高元素的索引来增加索引V。问题是,是否有一种简单的方法使R等待该索引的特定值?

2 个答案:

答案 0 :(得分:1)

假设您有一个消费者,您可以这样做:

    ...
    pthread_mutex_lock(&mtx);
    while (array.size() < targetSize) {
        pthread_cond_wait(&cv, &mtx);
    }
    // read data from array
    // remove data from array consumed
    pthread_mutex_unlock(&mtx);
    ...

答案 1 :(得分:1)

好的,所以基于聊天,我认为这里的问题不是如何正确地同步 ,而是如何最小化仍然无法进展的线程的虚假唤醒

(作为参考,这不是我从原始问题得到的印象)。

因此,我们可以做一个天真的实现,保留对哪些读者安排的明确控制......

#include <queue>
#include <thread>

// associate a blocked reader's desired index with the CV it waits on
struct BlockedReadToken {
    int index_;
    std::condition_variable cv_;
    explicit BlockedReadToken(int index) : index_(index) {}
};
struct TokenOrder {
    bool operator() (BlockedReadToken const *a,
                     BlockedReadToken const *b)
    {
        return a->index_ < b->index_;
    }
};

class BlockedReaderManager
{
    std::priority_queue<BlockedReadToken*,
                        std::vector<BlockedReadToken*>, TokenOrder> queue_;
public:
    // wait for the actual index to reach the required value
    void waitfor(std::unique_lock<std::mutex> &lock,
                 int required, int const &actual)
    {
        // NOTE: a good pooled allocator might be useful here
        // (note we only allocate while holding the lock anyway,
        // so no further synchronization is required)
        std::unique_ptr<BlockedReadToken> brt(new BlockedReadToken(required));
        queue_.push(brt.get());
        while (actual < required)
            brt->cv_.wait(lock);
    }
    // release every reader blocked waiting for the new actual index
    // (don't wake any whose condition isn't satisfied yet)
    void release(std::unique_lock<std::mutex> &lock, int actual)
    {
        while (!(queue_.empty() || queue_.top()->index_ > actual)) {
            queue_.top()->cv_.notify_one();
            queue_.pop();
        }
    }
};

围绕某个容器的包装器,它为读者使用这种阻塞机制:

template <typename RandomAccessContainer>
class ProgressiveContainer
{
    int size_;
    std::mutex mutex_;
    BlockedReaderManager blocked_;
    RandomAccessContainer container_;
public:
    typedef typename RandomAccessContainer::size_type size_type;
    typedef typename RandomAccessContainer::value_type value_type;

    void push_back(value_type const &val) {
        std::unique_lock<std::mutex> guard(mutex_);
        container_.push_back(val);
        ++size_;
        blocked_.release(guard, size_);
    }
    void check_readable(int index) {
        // could optimistically avoid locking with atomic size here?
        std::unique_lock<std::mutex> guard(mutex_);
        if (size_ < index)
            blocked_.waitfor(guard, index, size_);
    }
    // allow un-locked [] access and require reader to call check_readable?
    value_type& operator[](int index) {
        return container_[index];
    }
    value_type& at(int index) {
        check_readable(index);
        return container_[index];
    }
};