muduo网络库学习笔记(三)TimerQueue定时器队列


TimerQueue是EventLoop的组件之一,可以提供定时任务,和周期任务。

本章首先会简述关于timerfd系统定时函数的基本使用,和TimerQueue类的封装结构,最后给出TimerQueue::addTimer()接口的时序图与使用例子.

Linux中的时间函数

·time(2) / time_t(秒)

·ftime(3) / struct timeb(毫秒)

·gettimeofday(2) / struct timeval(微秒)

·clock_gettime(2) / struct timespec(纳秒)

还有gmtime / localtime / timegm / mktime / strftime / struct tm等与当

前时间无关的时间格式转换函数。

定时函数, 用于让程序等待一段时间或安排计划任务:·sleep(3)

·alarm(2)

·usleep(3)

·nanosleep(2)

·clock_nanosleep(2)

·getitimer(2) / setitimer(2)

·timer_create(2) / timer_settime(2) / timer_gettime(2) / timer_delete(2)

·timerfd_create(2) / timerfd_gettime(2) / timerfd_settime(2)

muduo中做的取舍如下

·(计时) 只使用gettimeofday(2)来获取当前时间。

·(定时) 只使用timerfd_*系列函数来处理定时任务。

gettimeofday(2)入选原因(这也是muduo::Timestamp class的主要设计考虑) :

1. time(2)的精度太低, ftime(3)已被废弃; clock_gettime(2)精度最高, 但是其系统调用的开销比gettimeofday(2)大。

2. 在x86-64平台上, gettimeofday(2)不是系统调用, 而是在用户态实现的, 没有上下文切换和陷入内核的开销32。

3. gettimeofday(2)的分辨率(resolution) 是1微秒, 现在的实现确实能达到这个计时精度, 足以满足日常计时的需要。 muduo::Timestamp用一个int64_t来表示从Unix Epoch到现在的微秒数, 其范围可达上下30万年。

timerfd_*入选的原因:

1. sleep(3) / alarm(2) / usleep(3)在实现时有可能用了SIGALRM信号, 在多线程程序中处理信号是个相当麻烦的事情, 应当尽量避免, 再说, 如果主程序和程序库都使用SIGALRM, 就糟糕了。

2. nanosleep(2)和clock_nanosleep(2)是线程安全的, 但是在非阻塞网络编程中, 绝对不能用让线程挂起的方式来等待一段时间, 这样一来程序会失去响应。 正确的做法是注册一个时间回调函数。

3. getitimer(2)和timer_create(2)也是用信号来deliver超时,在多线程程序中也会有麻烦。timer_create(2)可以指定信号的接收方是进程还是线程, 算是一个进步, 不过信号处理函数(signal handler) 能做的事情实在很受限。

4.timerfd_create(2)把时间变成了一个文件描述符, 该“文件”在定时器超时的那一刻变得可读, 这样就能很方便地融入select(2)/poll(2)框架中, 用统一的方式来处理IO事件和超时事件, 这也正是Reactor模式的长处。

5. 传统的Reactor利用select(2)/poll(2)/epoll(4)的timeout来实现定时功能, 但poll(2)和epoll_wait(2)的定时精度只有毫秒,远低于timerfd_settime(2)的定时精度。


timerfd简单使用介绍

本章使用到的两个系统函数:

#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec *old_value);

1、timerfd_create函数生成一个定时器,返回与之关联的文件描述,其中的clockid可以设成CLOCK_REALTIME和CLOCK_MONOTONIC

CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变

CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响

2、timerfd_settime用于启停定时器,new_value为超时时间,old_value为周期性定时时间,为0表示不进行周期性定时.

 struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
}; struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};

timerfd示例

通过select 监听timerfd,可读时表明到达定时时间.

#include <sys/timerfd.h>
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int main()
{ int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK |TFD_CLOEXEC); struct itimerspec howlong;
bzero(&howlong, sizeof howlong);
howlong.it_value.tv_sec = 3;
timerfd_settime(timerfd, 0, &howlong, NULL); fd_set rdset;
FD_ZERO(&rdset);
FD_SET(timerfd, &rdset); struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
while(1)
{
if(select(timerfd + 1, &rdset, NULL, NULL, &timeout) == 0)
{
std::cout << "timeout\n";
timeout.tv_sec = 1;
timeout.tv_usec = 0;
FD_SET(timerfd, &rdset);
continue;
}
std::cout << " timer happend\n";
break;
} close(timerfd); return 0;
} /* print
timeout
timeout
timer happend
*/

muduo中对timerfd的封装

int createTimerfd()
{
int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
TFD_NONBLOCK | TFD_CLOEXEC);
if (timerfd < 0)
{
std::cout << "Failed in timerfd_create" << std::endl;
abort();
}
return timerfd;
} struct timespec howMuchTimeFromNow(TimeStamp when)
{
int64_t microseconds = when.microSecondsSinceEpoch()
- TimeStamp::now().microSecondsSinceEpoch();
if (microseconds < 100)
{
microseconds = 100;
}
struct timespec ts;
ts.tv_sec = static_cast<time_t>(
microseconds / TimeStamp::kMicroSecondsPerSecond);
ts.tv_nsec = static_cast<long>(
(microseconds % TimeStamp::kMicroSecondsPerSecond) * 1000);
return ts;
} void readTimerfd(int timerfd, TimeStamp now)
{
uint64_t howmany;
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
std::cout << "TimerQueue::handleRead() " << howmany << " at " << now.toString() << std::endl;
if (n != sizeof howmany)
{
std::cout << "TimerQueue::handleRead() reads " << n << " bytes instead of 8" << std::endl;
}
} void resetTimerfd(int timerfd, TimeStamp expiration)
{
// wake up loop by timerfd_settime()
struct itimerspec newValue;
struct itimerspec oldValue;
bzero(&newValue, sizeof newValue);
bzero(&oldValue, sizeof oldValue);
newValue.it_value = howMuchTimeFromNow(expiration);
int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
if (ret)
{
std::cout << "timerfd_settime()" << std::endl;
}
}

TimerQueue的结构.

TimerQueue只提供了两个对外的接口

addTimer() 添加一个定时器超时执行回调函数cb. interval是周期性定时时间,本章不讲,置零不打开.

cancel() 取消一个定时器,本章也不细述,本章只介绍addTimer()接口.后面如果需要会补充本章.

TimerQueue定义如下

class TimerQueue
{
public:
TimerQueue(EventLoop* loop);
~TimerQueue(); // Schedules the callback to be run at given time, TimerId addTimer(const NetCallBacks::TimerCallBack& cb, TimeStamp when, double interval = 0.0); void cancel(TimerId timerId); private:
/*
.....
*/
EventLoop* p_loop;
const int m_timerfd;
Channel m_timerfdChannel; //Timer List sorted by expiration
TimerList m_timers;
ActiveTimerSet m_activeTimers;
};

虽然只提供了两个对外接口,但是私有成比较复杂,首先简介成员Timer.

Timer

Timer类作为TimerQueue的内部成员使用,封装一个定时器,addTimer()接口调用后生成一个Timer(),外头的回调函数和定时反应时间会封装再此Timer中.

Tiemr主要接口都是获取这些构造时配置的成员值

run() 调用回调函数.

expiration 定时器过期时间.

interval 周期性定时时间.

repeat 是否是周期性定时.

sequence 一个静态成员,用于记录Timer创建的个数. 为了保证它的线程安全性,使用AtomicInt64封装了一层原子操作.

TimerId 用于Cancel Timer的标志Id, 这两个类都不复杂,可自行参看muduo源码,也可翻我github上的SimpleMuduo单独类文件夹下的测试代码.

  Timer(const NetCallBacks::TimerCallBack& cb, TimeStamp when, double interval)
:m_callBack(cb),
m_expiration(when),
m_interval(interval),
m_repeat(interval > 0.0),
m_sequence(s_numCreated.incrementAndGet())
TimerId TimerQueue::addTimer(const NetCallBacks::TimerCallBack& cb, TimeStamp when, double interval)
{
Timer* timer = new Timer(cb, when, interval);
//p_loop->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));
return TimerId(timer, timer->sequence());
}

Timer的容器.

  typedef std::pair<TimeStamp, Timer*> Entry;
typedef std::set<Entry> TimerList;
typedef std::pair<Timer*, int64_t> ActiveTimer;
typedef std::set<ActiveTimer> ActiveTimerSet;

为了解决无法处理两个Timer到期时间相同的情况。使用了pair将时间戳和Timer的地址组成了一对.然后使用Set存储.

ActiveTimer 将Timer和sequence组成一对主要作用来索引迭代的.

TimerQueue私有接口介绍.

  void addTimerInLoop(Timer* timer);
void cancelInLoop(TimerId timerId);
//called when timerfd alarms
void handleRead();
//move out all expired timers and return they.
std::vector<Entry> getExpired(TimeStamp now);
bool insert(Timer* timer);
void reset(const std::vector<Entry>& expired, TimeStamp now);

添加定时器

bool insert(Timer* timer); //插入一个定时器.

EventLoop添加了一个runInLoop接口: 在它的IO线程内执行某个用户

任务回调, 即EventLoop::runInLoop(const Functor& cb), 其中Functor是

std::function<void()>。 如果用户在当前IO线程调用这个函数, 回调会

同步进行; 如果用户在其他线程调用runInLoop(), cb会被加入队列, IO

线程会被唤醒来调用这个Functor。

addTimer()使用了EventLoop的runInLoop接口(这个接口主要来保证线程安全性的,暂不讲),来执行addTimerInLoop().addTimerInLoop()调用insert()插入定时器.

如果此定时器是最早触发的那一个则会调用resetTimerfd()->timerfd_settime()启动定时器.

addTimer()->addTimerInLoop()->insert()->resetTimerfd()->timerfd_settime()

更新定时器

添加逻辑就在上面了,下面给出处理逻辑.

三个接口.

void handleRead(); //定时器触发回调.获取已过期的事件并处理随后更新列表.

std::vector getExpired(TimeStamp now);//获取一组已过期的定时器,并从TimerList中删除.

void reset(const std::vector& expired, TimeStamp now);//如果是周期性任务则重新配置时间插入,随后调用resetTimerfd()更新下一定时任务.

handleRead()->getExpired()->Timer->run(cb)->reset()->resetTimerfd()->timerfd_settime()

时序图

TimerQueue源码

#ifndef _NET_TIMERQUEUE_HH
#define _NET_TIMERQUEUE_HH
#include "TimerId.hh"
#include "CallBacks.hh"
#include "TimeStamp.hh"
#include "Channel.hh"
#include <set>
#include <vector> class EventLoop; class TimerQueue
{
public:
TimerQueue(EventLoop* loop);
~TimerQueue(); // Schedules the callback to be run at given time, TimerId addTimer(const NetCallBacks::TimerCallBack& cb, TimeStamp when, double interval = 0.0); void cancel(TimerId timerId); private:
typedef std::pair<TimeStamp, Timer*> Entry;
typedef std::set<Entry> TimerList;
typedef std::pair<Timer*, int64_t> ActiveTimer;
typedef std::set<ActiveTimer> ActiveTimerSet; void addTimerInLoop(Timer* timer);
void cancelInLoop(TimerId timerId);
//called when timerfd alarms
void handleRead();
//move out all expired timers and return they.
std::vector<Entry> getExpired(TimeStamp now);
bool insert(Timer* timer);
void reset(const std::vector<Entry>& expired, TimeStamp now); EventLoop* p_loop;
const int m_timerfd;
Channel m_timerfdChannel; //Timer List sorted by expiration
TimerList m_timers;
ActiveTimerSet m_activeTimers; bool m_callingExpiredTimers; /*atomic*/
ActiveTimerSet m_cancelingTimers; }; #endif //TimerQueeu.cpp #include <stdint.h>
#include <assert.h>
#include <sys/timerfd.h>
#include <unistd.h> #include "Logger.hh"
#include "EventLoop.hh"
#include "Timer.hh"
#include "TimerQueue.hh" namespace TimerFd
{ int createTimerfd()
{
int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
TFD_NONBLOCK | TFD_CLOEXEC);
if (timerfd < 0)
{
LOG_SYSFATAL << "Failed in timerfd_create";
}
return timerfd;
} struct timespec howMuchTimeFromNow(TimeStamp when)
{
int64_t microseconds = when.microSecondsSinceEpoch()
- TimeStamp::now().microSecondsSinceEpoch();
if (microseconds < 100)
{
microseconds = 100;
}
struct timespec ts;
ts.tv_sec = static_cast<time_t>(
microseconds / TimeStamp::kMicroSecondsPerSecond);
ts.tv_nsec = static_cast<long>(
(microseconds % TimeStamp::kMicroSecondsPerSecond) * 1000);
return ts;
} void readTimerfd(int timerfd, TimeStamp now)
{
uint64_t howmany;
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
if (n != sizeof howmany)
{
LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
}
} void resetTimerfd(int timerfd, TimeStamp expiration)
{
// wake up loop by timerfd_settime()
LOG_TRACE << "resetTimerfd()";
struct itimerspec newValue;
struct itimerspec oldValue;
bzero(&newValue, sizeof newValue);
bzero(&oldValue, sizeof oldValue);
newValue.it_value = howMuchTimeFromNow(expiration);
int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
if (ret)
{
LOG_SYSERR << "timerfd_settime()";
}
} }; using namespace TimerFd; TimerQueue::TimerQueue(EventLoop* loop)
:p_loop(loop),
m_timerfd(createTimerfd()),
m_timerfdChannel(p_loop, m_timerfd),
m_timers(),
m_callingExpiredTimers(false)
{
m_timerfdChannel.setReadCallBack(std::bind(&TimerQueue::handleRead, this));
m_timerfdChannel.enableReading();
} TimerQueue::~TimerQueue()
{
m_timerfdChannel.disableAll();
m_timerfdChannel.remove();
::close(m_timerfd);
for (TimerList::iterator it = m_timers.begin();
it != m_timers.end(); ++it)
{
delete it->second;
}
} std::vector<TimerQueue::Entry> TimerQueue::getExpired(TimeStamp now)
{
std::vector<Entry> expired;
Entry sentry = std::make_pair(now, reinterpret_cast<Timer*>UINTPTR_MAX);
TimerList::iterator it = m_timers.lower_bound(sentry);
assert(it == m_timers.end() || now < it->first);
std::copy(m_timers.begin(), it, back_inserter(expired));
m_timers.erase(m_timers.begin(), it); for(std::vector<Entry>::iterator it = expired.begin();
it != expired.end(); ++it)
{
ActiveTimer timer(it->second, it->second->sequence());
size_t n = m_activeTimers.erase(timer);
assert(n == 1); (void)n;
} assert(m_timers.size() == m_activeTimers.size()); return expired;
} TimerId TimerQueue::addTimer(const NetCallBacks::TimerCallBack& cb, TimeStamp when, double interval)
{
Timer* timer = new Timer(cb, when, interval);
p_loop->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));
return TimerId(timer, timer->sequence());
} void TimerQueue::addTimerInLoop(Timer* timer)
{
p_loop->assertInLoopThread();
bool earliestChanged = insert(timer); if (earliestChanged)
{
resetTimerfd(m_timerfd, timer->expiration());
}
} void TimerQueue::cancel(TimerId timerId)
{
p_loop->runInLoop(std::bind(&TimerQueue::cancelInLoop, this, timerId));
} void TimerQueue::cancelInLoop(TimerId timerId)
{
p_loop->assertInLoopThread();
assert(m_timers.size() == m_activeTimers.size());
ActiveTimer timer(timerId.m_timer, timerId.m_sequence);
ActiveTimerSet::iterator it = m_activeTimers.find(timer);
if(it != m_activeTimers.end())
{
size_t n = m_timers.erase(Entry(it->first->expiration(), it->first));
assert(n == 1);
delete it->first;
}
else if (m_callingExpiredTimers)
{
m_cancelingTimers.insert(timer);
}
assert(m_timers.size() == m_activeTimers.size());
} bool TimerQueue::insert(Timer* timer)
{
p_loop->assertInLoopThread();
assert(m_timers.size() == m_activeTimers.size());
bool earliestChanged = false;
TimeStamp when = timer->expiration();
TimerList::iterator it = m_timers.begin();
if (it == m_timers.end() || when < it->first)
{
earliestChanged = true;
}
{
std::pair<TimerList::iterator, bool> result
= m_timers.insert(Entry(when, timer));
assert(result.second); (void)result;
}
{
std::pair<ActiveTimerSet::iterator, bool> result
= m_activeTimers.insert(ActiveTimer(timer, timer->sequence()));
assert(result.second); (void)result;
} LOG_TRACE << "TimerQueue::insert() " << "m_timers.size() : "
<< m_timers.size() << " m_activeTimers.size() : " << m_activeTimers.size(); assert(m_timers.size() == m_activeTimers.size());
return earliestChanged;
} void TimerQueue::handleRead()
{
p_loop->assertInLoopThread();
TimeStamp now(TimeStamp::now());
readTimerfd(m_timerfd, now); std::vector<Entry> expired = getExpired(now); LOG_TRACE << "Expired Timer size " << expired.size() << " "; m_callingExpiredTimers = true;
m_cancelingTimers.clear(); for(std::vector<Entry>::iterator it = expired.begin();
it != expired.end(); ++it )
{
it->second->run();
} m_callingExpiredTimers = false; reset(expired, now);
} void TimerQueue::reset(const std::vector<Entry>& expired, TimeStamp now)
{
TimeStamp nextExpire; for(std::vector<Entry>::const_iterator it = expired.begin();
it != expired.end(); ++it)
{
ActiveTimer timer(it->second, it->second->sequence());
if(it->second->repeat()
&& m_cancelingTimers.find(timer) == m_cancelingTimers.end())
{//如果是周期定时器则重新设定时间插入. 否则delete.
it->second->restart(now);
insert(it->second);
}
else
{// FIXME move to a free list no delete please
delete it->second;
}
} if (!m_timers.empty())
{
nextExpire = m_timers.begin()->second->expiration();
} if (nextExpire.valid())
{
resetTimerfd(m_timerfd, nextExpire);
}
}

TimerQueue使用示例

测试TimerQueue的addTimer接口.

#include <errno.h>
#include <thread>
#include <strings.h>
#include <poll.h>
#include <functional>
#include "EventLoop.hh"
#include "Channel.hh"
#include "Poller.hh"
#include "Logger.hh"
#include "Timer.hh"
#include "TimeStamp.hh"
#include "TimerQueue.hh" EventLoop* g_loop; void print() { LOG_DEBUG << "test print()"; } void test()
{ LOG_DEBUG << "[test] : test timerQue"; } int main()
{
EventLoop loop;
g_loop = &loop; TimerQueue timerQue(&loop);
timerQue.addTimer(test, times::addTime(TimeStamp::now(), 3.0));
timerQue.addTimer(test, times::addTime(TimeStamp::now(), 3.0));
timerQue.addTimer(test, times::addTime(TimeStamp::now(), 5.0)); loop.loop(); return 0;
}
./test.out
2018-11-11 15:49:22.493990 [TRACE] [Poller.cpp:64] [updateChannel] fd= 3 events3
2018-11-11 15:49:22.494042 [TRACE] [EventLoop.cpp:34] [EventLoop] EventLoop Create 0x7FFFBD3C39B0 in thread 3262
2018-11-11 15:49:22.494047 [TRACE] [Poller.cpp:64] [updateChannel] fd= 5 events3
2018-11-11 15:49:22.494055 [TRACE] [TimerQueue.cpp:172] [insert] TimerQueue::insert() m_timers.size() : 1 m_activeTimers.size() : 1
2018-11-11 15:49:22.494058 [TRACE] [TimerQueue.cpp:55] [resetTimerfd] resetTimerfd()
2018-11-11 15:49:22.494066 [TRACE] [TimerQueue.cpp:172] [insert] TimerQueue::insert() m_timers.size() : 2 m_activeTimers.size() : 2
2018-11-11 15:49:22.494070 [TRACE] [TimerQueue.cpp:172] [insert] TimerQueue::insert() m_timers.size() : 3 m_activeTimers.size() : 3
2018-11-11 15:49:22.494073 [TRACE] [EventLoop.cpp:59] [loop] EventLoop 0x7FFFBD3C39B0 start loopig
2018-11-11 15:49:22.494075 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:23.495462 [TRACE] [Poller.cpp:28] [poll] nothing happended
2018-11-11 15:49:23.495488 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:24.496618 [TRACE] [Poller.cpp:28] [poll] nothing happended
2018-11-11 15:49:24.496640 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:25.494222 [TRACE] [Poller.cpp:24] [poll] 1 events happended
2018-11-11 15:49:25.494253 [TRACE] [TimerQueue.cpp:45] [readTimerfd] TimerQueue::handleRead() 1 at 1541922565.494251
2018-11-11 15:49:25.494316 [TRACE] [TimerQueue.cpp:188] [handleRead] Expired Timer size 2 2018-11-11 15:49:25.494320 [DEBUG] [main.cpp:50] [test] [test] : test timerQue
2018-11-11 15:49:25.494322 [DEBUG] [main.cpp:50] [test] [test] : test timerQue 2018-11-11 15:49:25.494325 [TRACE] [TimerQueue.cpp:55] [resetTimerfd] resetTimerfd()
2018-11-11 15:49:25.494332 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:26.496293 [TRACE] [Poller.cpp:28] [poll] nothing happended
2018-11-11 15:49:26.496318 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:27.494291 [TRACE] [Poller.cpp:24] [poll] 1 events happended
2018-11-11 15:49:27.494319 [TRACE] [TimerQueue.cpp:45] [readTimerfd] TimerQueue::handleRead() 1 at 1541922567.494317
2018-11-11 15:49:27.494328 [TRACE] [TimerQueue.cpp:188] [handleRead] Expired Timer size 1 2018-11-11 15:49:27.494331 [DEBUG] [main.cpp:50] [test] [test] : test timerQue 2018-11-11 15:49:27.494334 [TRACE] [Poller.cpp:20] [poll] Poller::poll()
2018-11-11 15:49:28.495665 [TRACE] [Poller.cpp:28] [poll] nothing happended

muduo网络库学习笔记(三)TimerQueue定时器队列的更多相关文章

  1. muduo网络库学习笔记(10):定时器的实现

    传统的Reactor通过控制select和poll的等待时间来实现定时,而现在在Linux中有了timerfd,我们可以用和处理IO事件相同的方式来处理定时,代码的一致性更好. 一.为什么选择time ...

  2. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...

  3. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  4. muduo 网络库学习之路(一)

    前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...

  5. muduo网络库学习之MutexLock类、MutexLockGuard类、Condition类、CountDownLatch类封装中的知识点

    一.MutexLock 类 class  MutexLock  :  boost::noncopyable 二.MutexLockGuard类 class  MutexLockGuard  :  bo ...

  6. Linux多线程服务端编程 使用muduo C++网络库 学习笔记 日志log

    代码来自陈硕开源代码库 muduo中 地址是https://github.com/chenshuo/muduo #pragma once #include <string> #define ...

  7. python网络编程学习笔记(三):socket网络服务器(转载)

    1.TCP连接的建立方法 客户端在建立一个TCP连接时一般需要两步,而服务器的这个过程需要四步,具体见下面的比较. 步骤 TCP客户端 TCP服务器 第一步 建立socket对象  建立socket对 ...

  8. muduo网络库架构总结

    目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...

  9. muduo网络库源码学习————Timestamp.cc

    今天开始学习陈硕先生的muduo网络库,moduo网络库得到很多好评,陈硕先生自己也说核心代码不超过5000行,所以我觉得有必要拿过来好好学习下,学习的时候在源码上面添加一些自己的注释,方便日后理解, ...

随机推荐

  1. 【转】Java学习---垃圾回收算法与 JVM 垃圾回收器综述

    [原文]https://www.toutiao.com/i6593931841462338062/ 垃圾回收算法与 JVM 垃圾回收器综述 我们常说的垃圾回收算法可以分为两部分:对象的查找算法与真正的 ...

  2. python解析式

    一.列表解析式 列表解析是外面一对中括号,它返回的是列表. 一般形式为:[expr for item in itratoble] print([i+1 for i in range(10)]) #结果 ...

  3. October 12th 2017 Week 41st Thursday

    Be happy for this moment. This moment is your life. 为这一刻感到高兴,这一刻是你的人生. Yesterday Tencent became Asia ...

  4. Python shutil.md

    shutil shutil模块包括高级文件操作,例如复制和归档. Copying Files shutil.copyfileobj(fsrc, fdst[, length]):将类似文件的对象fsrc ...

  5. php可逆加密解密

    函数: function encrypt($data, $key) { $prep_code = serialize($data); $block = mcrypt_get_block_size('d ...

  6. 使用mysql乐观锁解决并发问题思路

    本文摘自网络,仅供个人学习之用 案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后 ...

  7. 【转】浅谈React、Flux 与 Redux

    本文转自<浅谈React.Flux 与 Redux>,转载请注明出处. React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化 利用 props 形成 ...

  8. 测试udp服务的端口是否可用

    测试tcp服务的端口是否可用,可以使用: telnet ip port   但是如果这个用在upd服务上,就会报错, 因为telnet走的是tcp协议, 比如说192.168.80.131在8888端 ...

  9. shiro实战系列(九)之Web

    一.Configuration(配置) 将 Shiro 集成到任何 Web 应用程序的最简单的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何读取 Shir ...

  10. WorldWind源码剖析系列:二维点类Point2d和三维点类Point3d

    PluginSDK中的点主要有二维和三维两种类型,分别用来描述平面和立体点.其类图如下. 这两个类比较简单.其字段成员主要用来描述点对象在各坐标轴上的分量. 属性Length用来返回二维和三维点的距离 ...