目录
一:初始化线程池
二:多线程的业务处理
C/C++精品项目之图床共享云存储(1):基础组件-CSDN博客
C/C++精品项目之图床共享云存储(2):MySql连接池_mysql 连接池c++ ping-CSDN博客
C/C++精品项目之图床共享云存储(3):网络缓冲区类和main-CSDN博客
C/C++精品项目之图床共享云存储(4):注册,登录,token,排序文件-CSDN博客
C/C++精品项目之图床共享云存储(5):FastDFS存储原理,文件的秒传-CSDN博客
C/C++精品项目之图床共享云存储(6):图片的共享,浏览,获取,以及短链的生成-CSDN博客
C/C++精品项目之图床共享云存储(7):图片分享和浏览的优化,共享文件的排行榜-CSDN博客
一:初始化线程池
Linux下C++线程池的实现(分模块讲解)_linux c++实现线程池-CSDN博客
Linux下c语言的线程池(分模块讲解)_linux c线程池的作用-CSDN博客
上面是我讲过的两个线程池模型,今天使用的就是这个C++的模型,是可以直接拿来使用的。我们之前写的所有业务,全都是单线程的状态,现在我们加入线程池,变成多线程模式。
首先我们在主线程中,初始化线程池:我们使用init和start初始化了线程池,在线程池中,我们含有两个列表,一个是定时器列表,一个是loop事件列表。我们加入的这个回调函数就是放在了loop列表中,这个函数我会放在另外一个代码块中。
void http_loop_callback(void *callback_data, uint8_t msg, uint32_t handle,void *pParam) {UNUSED(callback_data);UNUSED(msg);UNUSED(handle);UNUSED(pParam);CHttpConn::SendResponseDataList(); // 静态函数, 将要发送的数据循环发给客户端//也就是在多线程中,将这些多个任务完成后的结果,按着id发送回去,为什么说是循环发送呢?,因为我们加入loop后,没有进行删除,所以一直存在。
}int initHttpConn(uint32_t thread_num) {g_thread_pool.Init(thread_num); // 初始化线程数量g_thread_pool.Start(); // 启动多线程,创建多线程netlib_add_loop(http_loop_callback,NULL); // http_loop_callback被epoll所在线程循环调用return 0;
}
首先我们回顾一下线程池和socket的一些东西,我们整体的运行是怎么样的:我们网络就是服务端进行listen,accept等待,然后进入到事件循环中进行epoll_wait,当客户端连接上来,触发读事件,执行了传入来的回调函数,并且重新设置了socket的回调函数(变成了http的回调函数了),接下来就是业务相关的处理,比如这里的登录,我们现在是多线程的,我们进行多个登录,当登录成功后,我们把这个登录信息,封装起来,放入一个链表中,也就是上面的:SendResponseDataList。这个函数是处理链表中的信息的,将他们的信息依次返回。
二:多线程的业务处理
我们不是开启多线程了吗,我们主线程主要是接收客户端的连接,分发任务到线程池中,然后线程池处理完任务后,将要返回的信息放到链表中,然后主线程将这些信息发送回去。也就是说线程池不处理发送消息的IO。
typedef struct {uint32_t conn_uuid; // 用于查找connectionstring resp_data; // 要回发的数据
} ResponsePdu_t;static HttpConnMap_t g_http_conn_map;
std::mutex CHttpConn::s_resp_mutex;
std::list<ResponsePdu_t *> CHttpConn::s_response_pdu_list;//主线程用来发送消息的链表
void CHttpConn::SendResponseDataList() {// LogInfo("into");// 发送数据s_resp_mutex.lock();while (!s_response_pdu_list.empty()) {ResponsePdu_t *pResp = s_response_pdu_list.front();s_response_pdu_list.pop_front();s_resp_mutex.unlock();CHttpConn *pConn = FindHttpConnByHandle(pResp->conn_uuid); // 该连接有可能已经被释放,如果被释放则返回NULLLogInfo("conn_uuid: {}", pResp->conn_uuid); //{0:x}if (pConn) {// LogInfo("send: {}", pResp->resp_data);pConn->Send((void *)pResp->resp_data.c_str(),pResp->resp_data.size()); // 最终socket send}delete pResp;s_resp_mutex.lock();}s_resp_mutex.unlock();
}//客户端用来给这个链表添加信息的
void CHttpConn::AddResponseData(uint32_t conn_uuid, string &resp_data) {LogInfo("into");ResponsePdu_t *pResp = new ResponsePdu_t;pResp->conn_uuid = conn_uuid;pResp->resp_data = std::move(resp_data);s_resp_mutex.lock();s_response_pdu_list.push_back(pResp);s_resp_mutex.unlock();
}
接下来我们看看具体的多线程业务:我们看到多线程中,并未直接封装后返回,而是将封装的json放入到了链表中,当我们这个事件触发完成后,就会将这个进行返回。
//多线程处理
void CHttpConn::_HandleLoginMRequest(string &url, string &post_data) {g_thread_pool.Exec(ApiUserLoginM, conn_handle_, post_data);
}//多线程处理
int ApiUserLoginM(u_int32_t conn_uuid, string &post_data) {string user_name;string pwd;string token;string resp_json;// 判断数据是否为空if (post_data.empty()) {encodeLoginJson(1, token, resp_json);goto END;}// 解析jsonif (decodeLoginJson(post_data, user_name, pwd) < 0) {LogError("decodeRegisterJson failed");encodeLoginJson(1, token, resp_json);goto END;}// 验证账号和密码是否匹配if (verifyUserPassword(user_name, pwd) < 0) {LogError("verifyUserPassword failed");encodeLoginJson(1, token, resp_json);goto END;}// 生成token并存储在redisif (setToken(user_name, token) < 0) {LogError("setToken failed");encodeLoginJson(1, token, resp_json);goto END;} else {encodeLoginJson(0, token, resp_json);goto END;}
END:char *str_content = new char[HTTP_RESPONSE_HTML_MAX];uint32_t ulen = resp_json.length();snprintf(str_content, HTTP_RESPONSE_HTML_MAX, HTTP_RESPONSE_HTML, ulen,resp_json.c_str());resp_json = str_content;CHttpConn::AddResponseData(conn_uuid, resp_json);delete str_content;return 0;
}//单线程处理
int ApiUserLogin(string &post_data, string &resp_json) {string user_name;string pwd;string token;// 判断数据是否为空if (post_data.empty()) {return -1;}// 解析jsonif (decodeLoginJson(post_data, user_name, pwd) < 0) {LogError("decodeRegisterJson failed");encodeLoginJson(1, token, resp_json);return -1;}// 验证账号和密码是否匹配if (verifyUserPassword(user_name, pwd) < 0) {LogError("verifyUserPassword failed");encodeLoginJson(1, token, resp_json);return -1;}// 生成token并存储在redisif (setToken(user_name, token) < 0) {LogError("setToken failed");encodeLoginJson(1, token, resp_json);return -1;} else {encodeLoginJson(0, token, resp_json);return 0;}
}
对于其他的一些业务,有些可以使用多线程,有些则不可以,我只是拿出了一个例子,其他的业务操作也是一样的。0voice · GitHub