讨论帖子:
http://bbs.csdn.net/topics/391009829
解决方案:
#include <algorithm>
#include <forward_list>
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <queue>
#include <random>
#include <string>
#include <unordered_map>
#include <utility>
using namespace std;
template < typename TTask >
class ManagedQueue
{
//typedefs
public:
typedef typename queue< TTask > TaskQueue;
typedef unordered_map< int, int > UserMap;
typedef forward_list< int > TaskSelections;
typedef unordered_map< int, TaskQueue > TaskQueues;
//Fields
public:
UserMap Users;
private:
TaskQueues tasks; //每个用户拥有一个TaskQueue
TaskSelections task_queues; //内含大量UID,其数量和用户配额相同
mt19937 rng; //随机数发生器
//Methods
private:
//向备选队列集合中随机插入用户ID,以便Dequeue时能按配额均匀取出任务
void RandomInsert(int user_id, int count)
{
uniform_int_distribution<int> dist(0, 2);
if (task_queues.empty())
{
for (int i = 0; i < count; i++)
{
task_queues.push_front(user_id);
}
}
else
{
while (count > 0)
{
for (auto i = task_queues.begin(); i != task_queues.end(); i++)
{
if (dist(rng) == 1)
{
task_queues.insert_after(i, user_id);
count--;
}
}
}
}
}
public:
//UserMap: UID -> 配额
ManagedQueue(const UserMap& user_table)
: Users(user_table), rng(random_device()())
{
//为每一个用户分配一个任务队列
for (auto& p : user_table)
{
tasks.emplace(p.first, queue< TTask >());
}
}
//如果添加任务前用户任务队列为空,就向备选队列集合中添加与配额相当数量的UID
void Enqueue(int user_id, const TTask& task)
{
auto& q = tasks[user_id];
bool insert_new = q.empty();
q.push(task);
if (insert_new)
{
RandomInsert(user_id, Users[user_id]);
}
}
//在备选队列中随机抽一个UID,并根据UID找到对应的任务队列,从任务队列中取出任务
//如果任务队列中没有任务,那么从备选队列集合中删除对应的UID
TTask Dequeue()
{
uniform_int_distribution<int> dist(0, distance(task_queues.begin(), task_queues.end()) - 1);
auto it = task_queues.begin();
advance(it, dist(rng));
auto& q = tasks[*it];
auto ret = q.front();
q.pop();
if (q.empty())
{
task_queues.remove_if([&](int user_id) { return tasks[user_id].empty(); });
}
return ret;
}
};
int main()
{
unordered_map<int, int> user = { {1001, 10}, {1002, 20}, {1003, 40}, {1004, 30} };
ManagedQueue<string> q(user);
for (int i = 0; i < 1000; i++)
{
q.Enqueue(1001 + i % 4, string("Task ") + to_string(i % 4 + 1));
}
for (int i = 0; i < 400; i++)
{
cout << q.Dequeue() << "\t";
}
return 0;
}