前来填坑。
背景:
先说一下双缓存的应用场景。消息中心不断的派发数据下来,一个线程接收派发下来的数据(成为接收线程),另一个线程对数据进行处理(成为工作线程)。很显然,我们期望接收数据的线程能够尽快把数据接收进来,同时工作线程也尽可能减少对接收线程的影响。举一个实际场景,用户连接管理,一方面要处理用户的连接请求,连接上的用户入队,另一方面不断的检测用户的心跳包,释放超时用户。
1.简单实现
一种比较容易想到的实现是用一个列表来缓存数据,有新用户连进来时加锁,入队,另一个线程里定时检测列表,超时的用户进行释放。代码如下
class Buffer
{
public:
void AddUser(int n)
{
// 有新用户时入队
std::lock_guard<std::mutex> lock(m_lock);
m_listUser.push_back(n);
}
void OnTimer()
{
std::lock_guard<std::mutex> lock(m_lock);
for (auto iter = m_listUser.begin(); iter != m_listUser.end();)
{
// 定时遍历所有用户,超时的释放掉
if (IsTimeout(*iter))
{
iter = m_listUser.erase(iter);
}
else
{
++iter;
}
}
}
bool IsTimeout(int n)
{
return false;
}
private:
std::mutex m_lock;
std::list<int> m_listUser;
};
这种方式简单,但是问题也很明显。检测时将列表锁住了 ,这时候新用户无法建立连接,如果用户数量很多,就造成了连接阻塞。
2.双缓存实现
将用户分为连接中用户和已连接用户。
class Buffer
{
public:
void AddUser(int n)
{
// 有新用户时加入到连接用户的队列
std::lock_guard<std::mutex> lock(m_lock);
m_listConnectingUser.push_back(n);
}
void OnTimer()
{
std::list<int> listTmp;
{
// 只对连接中的用户加锁,同时加锁操作只有一个swap,锁的竞争很小
std::lock_guard<std::mutex> lock(m_lock);
listTmp.swap(m_listConnectingUser);
}
for (auto iter = m_listConnectedUser.begin(); iter != m_listConnectedUser.end();)
{
// 只遍历已连接的用户
if (IsTimeout(*iter))
{
iter = m_listConnectedUser.erase(iter);
}
else
{
++iter;
}
}
// 连接中的用户专为已连接的用户
for (int tmp : listTmp)
{
m_listConnectedUser.push_back(tmp);
}
}
bool IsTimeout(int n)
{
return false;
}
private:
std::mutex m_lock;
std::list<int> m_listConnectingUser;
std::list<int> m_listConnectedUser;
};