个人主页小则又沐风个人专栏• [数据结构]• [竞赛专栏]• [C语言]• [C]• [Linux]• [OJ项目]• [MySQL]•[GIT]时间轮1.什么是时间轮我们先不来讲解什么是时间轮我们先来讲解一下我们在写代码的时候我们可能遇到的问题。我们需要实现一个功能模拟实现闹钟的工作方式来定时完成一些任务并且需要设计一个延迟执行、取消执行等功能。我们应该怎么实现这个功能呢这个看起来功能看起来实现是无从下手的。但是我们如果了解时间轮的话我们就可以有一点头绪了。时间轮的设计是这样的2.时间轮是怎么设计的我们将会设计一个指针这个指针将会在一个容器中移动在指针所在的位置下的任务将会被执行。所以我们只需要控制这个指针的移动速度就可以解决上述的问题。具体是怎么实现的呢我们先来说实现的大概逻辑我们使用的容器是数组然后我们的任务将会挂在这个数组上当指针走到对应的位置的时候我们就会让这个位置的任务自动执行。但是我们是怎么做到让任务进行自动执行的呢这让我想到了之前实现日志的操作了我们是这样设计的我们先封装一个任务的类然后我们将需要进行任务的回调函数设计进入这个类中然后我们把析构函数的行为改为执行这个回调函数。所以当我们每次的释放一个任务类的时候就是我们执行任务的时候。所以我们指针的行为就是释放。但是还有一个问题我们现在应该大概是解决了这个任务执行的逻辑了但是怎么去实现任务的延迟呢也就是我们收到一个信号之后我们怎么对这个任务执行的时间进行推迟呢首先我们明确的一点就是我们的任务执行的时间是和任务这个类实例化出的对象的生命周期有关的所以我们的思路就是延长这个对象的生命周期。所以我们将会引入智能指针。首先我们的任务对象将会是一个new出来的我们让一个智能指针拥有她这样我们的对象的销毁的条件就是持有这个对象的智能指针的个数是0。所以当我们进行延长执行任务的时间的时候我们的操作就是增加拥有对象智能指针的个数。这样我们就可以做到延迟任务了因为增加了引用计数之后我们第一次释放就不会引起析构函数。下面是一个简单的示例代码std::shared_ptrint pstd::make_sharedint(42); std::shared_ptrint p1p;3.解决细节问题现在我们大体的逻辑已经阐述明白了现在需要解决的就是一些细节上的问题了。首先我们上述的逻辑容器选择是一个数组但是更具体地是我们使用的是二维数组因为一维的数组只能挂上一个任务。其次我们如果设计的任务是在30s后执行的话我们该怎么进行设计呢我们就把这个数组地大小开辟到30.但是当我们的任务需要在一天后执行呢难道我们需要开辟的数组空间需要24*60*60这么大的嘛不是的。我们的设计可以更改成这样的我们设计三个数组。第一个是秒数组大小60第二个是分数组大小60第三个是时数组大小24我们的指针现在时数组中运动每次经过一个时的空间的时候就会检查这个小时内有没有挂有任务。如果有就进行进入分数组进行移动然后停在一个有任务的空间然后进入到秒数组中。4、代码实现#includeiostream #includevector #includeunordered_map #include functional #include memory #include unistd.h #include cstdint using TaskFun std::functionvoid(); using ReleaseFun std::functionvoid(); class Task { public: Task(uint64_t id,uint32_t timeout,const TaskFun fun) :_id(id) ,_timeout(timeout) ,_func(fun) ,_iscancel(false) { } ~Task() { if(_iscancelfalse) { _func(); } _release(); } void Setcancel() { _iscanceltrue; } void SetRelease(const ReleaseFun cb) { _releasecb; } uint32_t Timeout() { return _timeout; } private: uint64_t _id; //任务对象的ID uint32_t _timeout; //时间超限的设置 bool _iscancel; //是否取消任务的执行 TaskFun _func; //任务的回调函数 ReleaseFun _release; //清除在时间轮中的信息 }; class TimeWhell { public: void RvmTimer(uint64_t id) { auto it_timer.find(id); if(it!_timer.end()) { _timer.erase(it); } } public: void addtimewhell(uint64_t id,uint32_t timeout,const TaskFun fun) { TaskPtr p(new Task(id,timeout,fun)); p-SetRelease(std::bind(TimeWhell::RvmTimer,this,id)); _timer[id]WeakPtr(p); int pos (tickettimeout)%capacity; _Timewhell[pos].push_back(p); } void flush(uint64_t id) { auto it_timer.find(id); if(it!_timer.end()) { TaskPtr p1it-second.lock(); if(p1) { int pos (ticketp1-Timeout())%capacity; _Timewhell[pos].push_back(p1); } } return; } void cancel(uint64_t id) { auto it _timer.find(id); if(it!_timer.end()) { TaskPtr pit-second.lock(); if(p) { p-Setcancel(); } } return; } void Run() { ticket(ticket1)%capacity; _Timewhell[ticket].clear(); } TimeWhell() :ticket(0) ,capacity(60) ,_Timewhell(capacity) { } private: using TaskPtr std::shared_ptrTask; using WeakPtr std::weak_ptrTask; int ticket;//指针; int capacity;//大小 std::vectorstd::vectorTaskPtr _Timewhell; std::unordered_mapuint64_t,WeakPtr _timer; };正则表达式的介绍1、什么是正则表达式简单来说这个就是我们来解决字符串处理的工具。更加具体的来说就是这个工具能够在我们提供的字符串中提取到我们想要的部分。那么这有什么用呢实际上这个工具的用处超级的大。我们下面就以解析一个HTTP的ULR为例子来剖析这个工具的使用。2、如何使用正则表达式在我们进行编写网络通信服务器的时候尤其当我们使用的协议是HTTP协议的时候我们需要有一个工作就是需要对用户请求的URL进行分析处理。但是我们自己对这个字符串进行处理的时候我们难免都会觉得头痛。所以我们需要寻求一个工具来辅助我们完成这个工作。我们依赖的工具就是这个正则表达式。我们就以解析这个字符串为例子get /baidu/login?userxiaomingpass123123 HTTP/1.1\r\n首先呢我们想要使用这个工具就要包含这个工具的头文件。#includeregex三步学会使用正则表达式1.创建一个smatch对象这个对象就是我们之后按我们设定的需求提取字符串之后存储的地方。2.设置我们的提取要求这是我们使用正则表达式来提取字符串的最重要的步骤了。std::regex re( pattern, flags );第一个参数中需要我们自己来设计提取字符串的方法。第二个参数需要我们来设置提取字符串的时候需要遵守的规则。3根据上述的准备和目标字符串进行提取bool ret std::regex_match(str,smatch,re)这样我们就完成了。下面来详细讲解一下设置提取要求这一步。std::regex res((GET|HEAD|POST|PUT|DELETE) .*,std::regex::icase);在这里我们的设置是什么意思呢就是匹配括号里面的任意一个因为我们的URL第一个可能是其中的一个。后面的标记位设置是不区分大小写。那么后面的我们应该怎么提取呢([^?]*) [^?]代表的是提取非的字符后面的*表示的是可以及进行多次的匹配。\\?(.*) 提取非换行符的字符(HTTP\\/1\\.[01]) 提取HTTP/1.0或者HTTP/1.0。([01])代表的是匹配其中的一个。但是我上面的代码的原字符串是不包含末尾的换行符的。现在我们来加上去。std::regex res((GET|HEAD|POST|PUT|DELETE) ([^?]*)\\?(.*) (HTTP\\/1\\.[01])(?:\r\n)?,std::regex::icase);这样的(?:)?(?:)表示匹配某个格式字符串但是不提取,最后的表示的是匹配前边的表达式0次或1次因为有的URL是没有后面连续的一串的所以我们需要添加上一个匹配0次或1次。std::regex res((GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP\\/1\\.[01])(?:\n|\r\n)?,std::regex::icase);这是我们简单的提取的设置。下面是进行测试的代码#includeiostream #includeregex int main() { std::string strget /baidu/login?userxiaomingpass123123 HTTP/1.1\r\n; std::smatch matches; std::regex res((GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP\\/1\\.[01])(?:\n|\r\n)?,std::regex::icase); bool ret std::regex_match(str,matches,res); if (ret false) { return -1; } std::string method matches[1]; std::transform(method.begin(), method.end(), method.begin(), ::toupper); std::cout method std::endl; for (int i 0; i matches.size(); i) { std::cout i : ; std::cout matches[i] std::endl; } return 0; }