简体   繁体   English

如何在std :: deque中避免`deque iterator not dereferencable`? 锁?

[英]How to avoid `deque iterator not dereferencable` in a std::deque ? Locks?

Currently in my project I have two static methods PushObjects and ProcessObject . 目前在我的项目中我有两个静态方法PushObjectsProcessObject The PushObject method pushes back data to a static deque and this method can be accessed by multiple threads but the ProcessObject is always used by a single thread and is used to retrieve objects from the top and remove them.Now my problem is no matter what I try I always eventually (sooner or later get a deque iterator not dereferencable error.Any suggestions on what I could do to stop this issue. An abstract of my PushObjects and ProcessObject are given below PushObject方法将数据推送到静态双端队列,这个方法可以被多个线程访问,但ProcessObject总是由一个线程使用,用于从顶部检索对象并删除它们。现在我的问题是无论我是什么尝试我总是最终(迟早会得到一个deque iterator not dereferencable error.Any建议我可以做什么来阻止这个问题。我的PushObjectsProcessObject的摘要在下面给出

    void foo::PushObjects(obj A)
    {
        try
        {
            {//Begin Lock
                boost::lock_guard<boost::mutex> lock(mutex_push);
                mydeque.push_back(A);
            }//End Lock
            condition_read.notify_one(); //Inform the reader that it could start reading 
        }
        catch (std::exception& e)
        {
            __debugbreak();
        }
    }


    This is the static Reader method

    void foo::ProcessObject()
    {
        {//Begin Lock
            boost::unique_lock<boost::mutex> lock(mutex_process);
            while(true)
            {
                    while(mydeque.empty()) { condition_read.wait(lock); }
                    try
                    {
                        if(!mydeque.empty())
                        {
                                obj a = mydeque.front();
                                ......Process the object........
                                mydeque.pop_front();
                        }

                    }
                    catch (std::exception& e)
                    {
                        __debugbreak();
                    }
            }//end while
        }//End lock
    }

From what I have read is that iterators become invalid once items are added or removed from the deque. 从我所读到的是,一旦在双端队列中添加或删除项目,迭代器就会变为无效。 Is there a way to resolve this issue. 有没有办法解决这个问题。

It looks like you aren't using the same mutex ( mutex_push vs mutex_process ) for reading and writing from/to the deque . 看起来你没有使用相同的互斥锁( mutex_push vs mutex_process )来读取/写入deque You need to be. 你需要。 It's not safe to write to memory and read from it at the same time on different threads. 写入内存并在不同线程上同时读取内存是不安全的。

Other notes: 其他说明:

obj a = mydeque.front();
......Process the object........
mydeque.pop_front();

could be much better if you manage your lock to have the minimum lock time... 如果你管理你的锁具有最小的锁定时间可能会好得多...

obj a = std::move(mydeque.front());
mydeque.pop_front();
lock.unlock();
// process the object
lock.lock();

You likely don't need to be locked (or at least don't need the same lock) to process the object. 您可能不需要锁定(或至少不需要相同的锁)来处理对象。 This way your writer can still write to the deque while you are processing. 通过这种方式,您的编写者仍然可以在处理时写入双端队列。 Also to note is that there is nothing stopping you here from being multi producer multi consumer instead of just multi producer single consumer. 另外需要注意的是,没有什么可以阻止你成为多生产者多消费者,而不仅仅是多生产者单一消费者。

To expand Dave's answer regarding unlocking as soon as possible to allow concurrent writes while you are processing your items... 在您处理项目时,尽快扩展Dave关于解锁的答案以允许并发写入...

Your deque may well contain several items (which have been pushed eg. while you were still processing another item). 你的双端队列可能包含几个项目(例如在你还在处理另一个项目的时候已被推送)。 In order to avoid locking for each and every item you could just swap your deque with an empty, local one and process your items from that local deque. 为了避免锁定每个项目,你可以将你的双端队列交换为空的本地项目,并处理来自该本地双端队列的项目。 Some code will make it clearer: 有些代码会更清晰:

while (true) {
    std::deque<Obj> tmp_deque;
    {
        std::unique_lock<std::mutex> lock(mutex);
        while (mydeque.empty())
            condition.wait(lock);
        mydeque.swap(tmp_deque);
    }
    while (!tmp_deque.empty()) {
        Obj obj = std::move(tmp_deque.front());
        tmp_deque.pop_front();
        // process obj
    }
}

This way, instead of lock / get 1 item / unlock / process 1 item you end up with lock / get all items / unlock / process all items which is much more efficient since locking mutexes are a big performance hit. 这样,代替锁定/获取1项目/解锁/处理1项目,您最终获得锁定/获取所有项目/解锁/处理所有项目 ,因为锁定互斥锁是一个很大的性能影响,因此效率更高。

Clearly, this is only for a single-consumer model . 显然, 这仅适用于单一消费者模式 If you have multiple consumers you really don't want to enqueue all items in a single consumer and let all the other consumers go idle. 如果您有多个消费者,您实际上不希望将单个消费者中的所有项目排入队列,并让所有其他消费者闲置。

You need to have single mutex for accessing mydeque no read/write mutexes. 您需要使用单个互斥锁来访问mydeque没有读/写互斥锁。 And any access to deque must be while mutex locked. 任何对双端队列的访问都必须在互斥锁定时进行。 Even you just checking empty(). 即使你只是检查空()。 Since deque operations are not atomic you may end up with mydeque.empty() returning false, while being in some seminonempty state in a middle of push_back. 由于deque操作不是原子操作,因此最终mydeque.empty()返回false,而在push_back中间处于某种半空状态。 Thus you need boost::lock_guard<boost::mutex> lock(mutex_push); 因此你需要boost::lock_guard<boost::mutex> lock(mutex_push); prior to each access to the mydeque. 在每次访问mydeque之前。 or during whole operation that changes deque content. 或在整个操作过程中改变双端内容。

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

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