【问题标题】:How to avoid `deque iterator not dereferencable` in a std::deque ? Locks?如何避免 std::deque 中的`deque iterator not dereferencable`?锁?
【发布时间】:2013-04-10 04:21:40
【问题描述】:

目前在我的项目中,我有两个静态方法PushObjectsProcessObjectPushObject 方法将数据推回静态双端队列,该方法可以由多个线程访问,但 ProcessObject 始终由单个线程使用,用于从顶部检索对象并删除它们。现在我的问题是无论我尝试什么,我最终总是(迟早会收到deque iterator not dereferencable 错误。任何关于我可以做些什么来阻止这个问题的建议。下面给出了我的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
    }

据我所知,一旦从双端队列中添加或删除项目,迭代器就会变得无效。有没有办法解决这个问题。

【问题讨论】:

  • boost::mutex::scoped_lock lock(lock); 显然是错误的。如果您删除它,并取消注释上面行中更合理的锁定声明,您仍然有问题吗?
  • Re "iterators become invalid": 没错,但是你这里没有使用任何迭代器,所以这不是问题。
  • 好的,但是你的真实代码是否锁定mutex_push(就像注释掉的行一样),或者它是否在没有触及mutex_push的情况下错误地从自身初始化lock(作为你的代码)已经发布了)?
  • 仅供参考,if(!mydeque.empty()) 是不需要的,除非你有代码在修改你的双端队列而不锁定你的双端队列互斥锁(如果你这样做,那就是你没有做对的线索) .看代码。为什么你刚刚打破了监视 cond-var 的 while() 循环?嗯...此外,方便省略的超级机密代码是否仅称为......Process the object........ 曾经解锁双端队列锁?请发布真实代码

标签: c++ iterator mutex deque


【解决方案1】:

您似乎没有使用相同的互斥体(mutex_pushmutex_process)来读写 deque。你需要。在不同线程上同时写入和读取内存是不安全的。

其他说明:

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

如果你管理你的锁有最短的锁定时间会更好......

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

您可能不需要被锁定(或至少不需要相同的锁)来处理对象。这样,您的编写器仍然可以在您处理时写入双端队列。另外需要注意的是,这里没有什么可以阻止您成为多生产者多消费者,而不仅仅是多生产者单消费者。

【讨论】:

  • 因此,如果我同时使用单个互斥锁,那么写入和读取将不会同时进行。目前是我正确吗?
  • @Rajeshwar 你不能同时读写。你最终会在写到一半时阅读并得到一些乱码。这就是互斥锁存在的原因......所以你可以导致等待,这样你就不会读写或同时写入和写入
  • 谢谢。我今天要试一试——已经重构了我的代码。现在我将使用单个静态互斥体进行读写。
【解决方案2】:

扩展 Dave 关于尽快解锁的答案,以便在您处理项目时允许并发写入...

您的双端队列可能包含多个项目(例如,当您仍在处理另一个项目时,这些项目已被推送)。为了避免锁定每个项目,您可以将您的双端队列与一个空的本地队列交换,并从该本地双端队列处理您的项目。一些代码会让它更清晰:

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
    }
}

这样,lock / get 1 item / unlock / process 1 item 你最终会得到 lock / get all items / unlock / process all items,而不是 lock / get all items / unlock / process all items效率更高,因为锁定互斥锁会对性能造成很大影响。

显然,这仅适用于单一消费者模式。如果您有多个消费者,您真的不希望将所有项目排入单个消费者的队列中并让所有其他消费者闲置。

【讨论】:

    【解决方案3】:

    您需要有单个互斥锁来访问mydeque,没有读/写互斥锁。并且对双端队列的任何访问都必须在互斥锁锁定时进行。即使您只是检查 empty()。由于双端队列操作不是原子的,你最终可能会以mydeque.empty() 返回 false,同时在 push_back 中间处于某种半空状态。因此,在每次访问 mydeque 之前,您都需要 boost::lock_guard&lt;boost::mutex&gt; lock(mutex_push);。或在更改双端队列内容的整个操作期间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-28
      • 2021-07-15
      • 2012-11-09
      • 2015-12-12
      • 2012-09-23
      • 2015-01-15
      相关资源
      最近更新 更多