folly库TimeoutQueue

boost::multi_index_container是Boost库中的一个重要组件,它定义了一个具有多个索引方式的容器,使得用户可以从不同的维度对元素进行索引、排序和存取。以下是对boost::multi_index_container的详细介绍:

一、基本概念

boost::multi_index_container是一个模板类,它允许在同一个容器中维护多个索引,每个索引都可以用于不同的排序和访问方式。这些索引可以是唯一的,也可以是非唯一的,类似于数据库中的主键和次键。

二、索引类型

boost::multi_index_container支持多种索引类型,包括但不限于:

  1. 有序索引(ordered_index):根据元素的某个属性进行排序。有序索引可以是唯一的(ordered_unique),也可以是非唯一的(ordered_non_unique)。
  2. 哈希索引(hashed_index):基于哈希表实现,提供快速的元素查找功能。
  3. 序列索引(sequenced_index):保持元素插入的顺序,类似于std::vectorstd::deque

三、使用示例

以下是一个简单的使用示例,展示了如何定义一个boost::multi_index_container,并基于学生信息结构体实现按ID、姓名和分数排序:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <iostream>
#include <string>

using namespace std;

// 定义学生信息结构体
struct student {
    int id;
    string name;
    int score;
    string remark;

    student(int id, string name, int score, string remark)
        : id(id), name(name), score(score), remark(remark) {}

    void print() const {
        cout << "id:" << id << " name:" << name << " score:" << score << endl;
    }
};

// 定义用于排序的空结构体对象,作为索引的标签
struct _id {};
struct _name {};
struct _score {};

// 定义一个multi_index_container(多索引容器)
using student_table = boost::multi_index::multi_index_container<
    student,
    boost::multi_index::indexed_by<
        boost::multi_index::ordered_unique<
            boost::multi_index::tag<_id>,
            BOOST_MULTI_INDEX_MEMBER(student, int, id)
        >, // ID为唯一索引,类似主键
        boost::multi_index::ordered_non_unique<
            boost::multi_index::tag<_name>,
            BOOST_MULTI_INDEX_MEMBER(student, string, name)
        >, // 非唯一索引
        boost::multi_index::ordered_non_unique<
            boost::multi_index::tag<_score>,
            BOOST_MULTI_INDEX_MEMBER(student, int, score)
        >
    >
>;

int main() {
    // 初始化数据
    student_table allStu;
    allStu.insert(student(1, "lili", 85, "hello"));
    allStu.insert(student(2, "liming", 90, "hello"));
    allStu.insert(student(3, "xiaoming", 65, "hello"));
    allStu.insert(student(4, "ergou", 80, "hello"));
    allStu.insert(student(5, "dagou", 60, "hello"));

    // 按ID排序并输出
    student_table::index<_id>::type& sort_id = allStu.get<0>();
    cout << "sort by student id:" << endl;
    for (auto iter_id = sort_id.begin(); iter_id != sort_id.end(); iter_id++) {
        iter_id->print();
    }

    // 按姓名排序并输出
    student_table::index<_name>::type& sort_name = allStu.get<1>();
    cout << "\nsort by student name:" << endl;
    for (auto iter_name = sort_name.begin(); iter_name != sort_name.end(); iter_name++) {
        iter_name->print();
    }

    // 按分数排序并输出
    student_table::index<_score>::type& sort_score = allStu.get<2>();
    cout << "\nsort by student score:" << endl;
    for (auto iter_score = sort_score.begin(); iter_score != sort_score.end(); iter_score++) {
        iter_score->print();
    }

    return 0;
}

四、注意事项

  1. 唯一性约束:当定义唯一索引时(如ordered_unique),插入或替换元素时必须确保新值的主键字段值的唯一性,否则insert()replace()操作会失败。
  2. 数据修改:修改数据之后,必须通过replace()方法提交修改,以更新各个索引。不能直接通过迭代器修改元素的值,因为这可能会在不通知容器的情况下引入不一致性。
  3. 索引标签:为索引增加标签可以方便地通过标签名访问索引,而不需要记住索引的序号。同时,一个索引项可以有多个标签。

综上所述,boost::multi_index_container是一个功能强大的多索引容器,它提供了灵活且高效的元素索引、排序和访问方式。通过合理使用该容器,可以大大提高程序的性能和可维护性。

1,TimeoutQueue头文件代码

/*
 * Copyright 2011-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Simple timeout queue.  Call user-specified callbacks when their timeouts
 * expire.
 *
 * This class assumes that "time" is an int64_t and doesn't care about time
 * units (seconds, milliseconds, etc).  You call runOnce() / runLoop() using
 * the same time units that you use to specify callbacks.
 *
 * @author Tudor Bosman (tudorb@fb.com)
 */

#pragma once

#include <cstdint>
#include <functional>

#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>

namespace folly {

class TimeoutQueue {
 public:
  typedef int64_t Id;
  typedef std::function<void(Id, int64_t)> Callback;

  TimeoutQueue() : nextId_(1) { }

  /**
   * Add a one-time timeout event that will fire "delay" time units from "now"
   * (that is, the first time that run*() is called with a time value >= now
   * + delay).
   */
  Id add(int64_t now, int64_t delay, Callback callback);

  /**
   * Add a repeating timeout event that will fire every "interval" time units
   * (it will first fire when run*() is called with a time value >=
   * now + interval).
   *
   * run*() will always invoke each repeating event at most once, even if
   * more than one "interval" period has passed.
   */
  Id addRepeating(int64_t now, int64_t interval, Callback callback);

  /**
   * Erase a given timeout event, returns true if the event was actually
   * erased and false if it didn't exist in our queue.
   */
  bool erase(Id id);

  /**
   * Process all events that are due at times <= "now" by calling their
   * callbacks.
   *
   * Callbacks are allowed to call back into the queue and add / erase events;
   * they might create more events that are already due.  In this case,
   * runOnce() will only go through the queue once, and return a "next
   * expiration" time in the past or present (<= now); runLoop()
   * will process the queue again, until there are no events already due.
   *
   * Note that it is then possible for runLoop to never return if
   * callbacks re-add themselves to the queue (or if you have repeating
   * callbacks with an interval of 0).
   *
   * Return the time that the next event will be due (same as
   * nextExpiration(), below)
   */
  int64_t runOnce(int64_t now) { return runInternal(now, true); }
  int64_t runLoop(int64_t now) { return runInternal(now, false); }

  /**
   * Return the time that the next event will be due.
   */
  int64_t nextExpiration() const;

 private:
  int64_t runInternal(int64_t now, bool runOnce);
  // noncopyable
  TimeoutQueue(const TimeoutQueue&) = delete;
  TimeoutQueue& operator=(const TimeoutQueue&) = delete;

  struct Event {
    Id id;
    int64_t expiration;
    int64_t repeatInterval;
    Callback callback;
  };

  typedef boost::multi_index_container<
    Event,
    boost::multi_index::indexed_by<
      boost::multi_index::ordered_unique<boost::multi_index::member<
        Event, Id, &Event::id
      >>,
      boost::multi_index::ordered_non_unique<boost::multi_index::member<
        Event, int64_t, &Event::expiration
      >>
    >
  > Set;

  enum {
    BY_ID=0,
    BY_EXPIRATION=1
  };

  Set timeouts_;
  Id nextId_;
};

} // namespace folly

源文件代码

/*
 * Copyright 2011-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/TimeoutQueue.h>
#include <algorithm>
#include <vector>

namespace folly {

TimeoutQueue::Id
TimeoutQueue::add(int64_t now, int64_t delay, Callback callback) {
  Id id = nextId_++;
  timeouts_.insert({id, now + delay, -1, std::move(callback)});
  return id;
}

TimeoutQueue::Id
TimeoutQueue::addRepeating(int64_t now, int64_t interval, Callback callback) {
  Id id = nextId_++;
  timeouts_.insert({id, now + interval, interval, std::move(callback)});
  return id;
}

int64_t TimeoutQueue::nextExpiration() const {
  return (
      timeouts_.empty() ? std::numeric_limits<int64_t>::max()
                        : timeouts_.get<BY_EXPIRATION>().begin()->expiration);
}

bool TimeoutQueue::erase(Id id) {
  return timeouts_.get<BY_ID>().erase(id);
}

int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) {
  auto& byExpiration = timeouts_.get<BY_EXPIRATION>();
  int64_t nextExp;
  do {
    const auto end = byExpiration.upper_bound(now);
    std::vector<Event> expired;
    std::move(byExpiration.begin(), end, std::back_inserter(expired));
    byExpiration.erase(byExpiration.begin(), end);
    for (const auto& event : expired) {
      // Reinsert if repeating, do this before executing callbacks
      // so the callbacks have a chance to call erase
      if (event.repeatInterval >= 0) {
        timeouts_.insert({event.id,
                          now + event.repeatInterval,
                          event.repeatInterval,
                          event.callback});
      }
    }

    // Call callbacks
    for (const auto& event : expired) {
      event.callback(event.id, now);
    }
    nextExp = nextExpiration();
  } while (!onceOnly && nextExp <= now);
  return nextExp;
}

} // namespace folly

这个消息队列最核心就是这个循环

int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) {
  auto& byExpiration = timeouts_.get<BY_EXPIRATION>();
  int64_t nextExp;
  do {
    const auto end = byExpiration.upper_bound(now);
    std::vector<Event> expired;
    std::move(byExpiration.begin(), end, std::back_inserter(expired));
    byExpiration.erase(byExpiration.begin(), end);
    for (const auto& event : expired) {
      // Reinsert if repeating, do this before executing callbacks
      // so the callbacks have a chance to call erase
      if (event.repeatInterval >= 0) {
        timeouts_.insert({event.id,
                          now + event.repeatInterval,
                          event.repeatInterval,
                          event.callback});
      }
    }

    // Call callbacks
    for (const auto& event : expired) {
      event.callback(event.id, now);
    }
    nextExp = nextExpiration();
  } while (!onceOnly && nextExp <= now);
  return nextExp;
}

先找到所有时间戳的结合体,然后判断和执行回调

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值