C ++ boost thread:根据使用并发队列的互斥/条件使工作线程暂停和取消暂停

时间:2012-06-14 19:56:05

标签: c++ multithreading boost mutex

我对多线程编程很新,所以请原谅我可能不精确的问题。这是我的问题:

我有一个函数处理数据并生成大量相同类型的对象。这是在几个嵌套循环中迭代完成的,因此只进行所有迭代,将这些对象保存在某个容器中然后在接口代码中处理该容器以执行后续步骤是切实可行的。但是,我必须创建数百万这些会破坏内存使用量的对象。这些限制主要是由于我无法控制的外部因素。

仅生成一定数量的数据将是理想的,但是打破循环并稍后在同一点重新启动也是不切实际的。我的想法是在一个单独的线程中进行处理,该线程将在n次迭代后暂停,并在所有n个对象完全处理后恢复,然后恢复,执行下一次迭代,依此类推,直到完成所有迭代。重要的是等到线程完成所有n次迭代,因此两个线程都不会真正并行运行。

这是我的问题开始的地方:如何正确锁定互斥锁?我的方法产生boost :: lock_errors。以下是一些显示我想要做的代码:

boost::recursive_mutex bla;
boost::condition_variable_any v1;
boost::condition_variable_any v2;
boost::recursive_mutex::scoped_lock lock(bla);
int got_processed = 0;
const int n = 10;

void ProcessNIterations() {
  got_processed = 0;
  // have some mutex or whatever unlocked here so that the worker thread can 
  // start or resume. 
  // my idea: have some sort of mutex lock that unlocks here and a condition
  // variable v1 that is notified while the thread is waiting for that.
  lock.unlock();
  v1.notify_one();

  // while the thread is working to do the iterations this function should wait
  // because there is no use to proceed until the n iterations are done
  // my idea: have another condition v2 variable that we wait for here and lock
  // afterwards so the thread is blocked/paused
  while (got_processed < n) {
    v2.wait(lock);
  }
}

void WorkerThread() {
  int counter = 0;
  // wait for something to start
  // my idea: acquire a mutex lock here that was locked elsewhere before and 
  // wait for ProcessNIterations() to unlock it so this can start
  boost::recursive_mutex::scoped_lock internal_lock(bla);

  for (;;) {
    for (;;) {
      // here do the iterations
      counter++;
      std::cout << "iteration #" << counter << std::endl;
      got_processed++;

      if (counter >= n) {
        // we've done n iterations; pause here
        // my idea: unlock the mutex, notify v2
        internal_lock.unlock();
        v2.notify_one();

        while (got_processed > 0) {
          // when ProcessNIterations() is called again, resume here
          // my idea: wait for v1 reacquiring the mutex again
          v1.wait(internal_lock);
        }
        counter = 0;
      }
    }
  }
}

int main(int argc, char *argv[]) {
  boost::thread mythread(WorkerThread);

  ProcessNIterations();
  ProcessNIterations();

  while (true) {}
}

上述代码在行v2.wait(lock);中执行10次迭代后失败,并显示以下消息:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::lock_error> >'
  what():  boost::lock_error

我该如何正确地做到这一点?如果这是要走的路,我该如何避免lock_errors?

编辑:我使用与here讨论过的并发队列解决了这个问题。此队列还具有最大大小,之后push将等待至少一个元素已被pop编辑。因此,生产者工作者可以简单地继续填充此队列,其余代码可以pop条目,因为它是合适的。不需要在队列外部进行互斥锁定。队列在这里:

template<typename Data>
class concurrent_queue
{
private:
  std::queue<Data> the_queue;
  mutable boost::mutex the_mutex;
  boost::condition_variable the_condition_variable;
  boost::condition_variable the_condition_variable_popped;
  int max_size_;
public:
  concurrent_queue(int max_size=-1) : max_size_(max_size) {}

  void push(const Data& data) {
    boost::mutex::scoped_lock lock(the_mutex);

    while (max_size_ > 0 && the_queue.size() >= max_size_) {
      the_condition_variable_popped.wait(lock);
    }

    the_queue.push(data);
    lock.unlock();
    the_condition_variable.notify_one();
  }

  bool empty() const {
    boost::mutex::scoped_lock lock(the_mutex);
    return the_queue.empty();
  }

  bool wait_and_pop(Data& popped_value) {
    boost::mutex::scoped_lock lock(the_mutex);
    bool locked = true;
    if (the_queue.empty()) {
      locked = the_condition_variable.timed_wait(lock, boost::posix_time::seconds(1));
    }

    if (locked && !the_queue.empty()) {
      popped_value=the_queue.front();
      the_queue.pop();
      the_condition_variable_popped.notify_one();
      return true;
    } else {
      return false;
    }
  }

  int size() {
    boost::mutex::scoped_lock lock(the_mutex);
    return the_queue.size();
  }
};

2 个答案:

答案 0 :(得分:1)

这可以使用条件变量来实现。一旦执行了N次迭代,就在条件变量上调用wait(),当在另一个线程中处理对象时,在条件变量上调用signal()来解除阻塞在条件变量上阻塞的另一个线程。 / p>

答案 1 :(得分:0)

您可能希望将某种有限容量队列列表或堆栈与条件变量结合使用。当队列已满时,生产者线程等待条件变量,并且当用户线程从队列中删除元素时,它会发出条件变量的信号。这将允许生产者唤醒并再次填满队列。如果你真的想一次处理N个元素,那么只有当队列中有N个元素的容量时,工作者才会发出信号,而不是每当他们将一个项目从队列中拉出时。