前言

C++11之前我们使用线程需要系统提供API、posix线程库或者使用boost提供的线程库,C++11后就加入了跨平台的线程类std::thread,线程同步相关类std::mutex、std::lock_guard、std::condition_variable、std::atomic以及异步操作相关类std::async、std::future、std::promise等等,这使得我们编写跨平台的多线程程序变得容易,线程的一个高级应用就是线程池,使用线程池可以充分利用多核CPU的并行计算能力,以及避免了使用单个线程的创建和销毁的开销,所以线程池在实际项目中用的很广泛,很多RPC框架都是用了线程池来处理事务,比如说Thrifteasyrpc等等,接下来我们将使用C++11来实现一个通用的半同步半异步线程池(个人博客也发表了《使用C++11实现一个半同步半异步线程池》)。

实现

一个半同步半异步线程池分为三层。

  1. 同步服务层:它处理来自上层的任务请求,上层的请求可能是并发的,这些请求不是马上就会被处理的,而是将这些任务放到一个同步排队层中,等待处理。
  2. 同步排队层: 来自上层的任务请求都会加到排队层中等待处理,排队层实际就是一个std::queue。
  3. 异步服务层: 这一层中会有多个线程同时处理排队层中的任务,异步服务层从同步排队层中取出任务并行的处理。

这三个层次之间需要使用std::mutex、std::condition_variable来进行事件同步,线程池的实现代码如下。

#ifndef _THREADPOOL_H
#define _THREADPOOL_H #include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <memory>
#include <functional>
#include <condition_variable>
#include <atomic>
#include <type_traits> static const std::size_t max_task_quque_size = 100000;
static const std::size_t max_thread_size = 30; class thread_pool
{
public:
using work_thread_ptr = std::shared_ptr<std::thread>;
using task_t = std::function<void()>; explicit thread_pool() : _is_stop_threadpool(false) {} ~thread_pool()
{
stop();
} void init_thread_num(std::size_t num)
{
if (num <= 0 || num > max_thread_size)
{
std::string str = "Number of threads in the range of 1 to " + std::to_string(max_thread_size);
throw std::invalid_argument(str);
} for (std::size_t i = 0; i < num; ++i)
{
work_thread_ptr t = std::make_shared<std::thread>(std::bind(&thread_pool::run_task, this));
_thread_vec.emplace_back(t);
}
} // 支持普通全局函数、静态函数、以及lambda表达式
template<typename Function, typename... Args>
void add_task(const Function& func, Args... args)
{
if (!_is_stop_threadpool)
{
// 用lambda表达式来保存函数地址和参数
task_t task = [&func, args...]{ return func(args...); };
add_task_impl(task);
}
} // 支持函数对象(仿函数)
template<typename Function, typename... Args>
typename std::enable_if<std::is_class<Function>::value>::type add_task(Function& func, Args... args)
{
if (!_is_stop_threadpool)
{
task_t task = [&func, args...]{ return func(args...); };
add_task_impl(task);
}
} // 支持类成员函数
template<typename Function, typename Self, typename... Args>
void add_task(const Function& func, Self* self, Args... args)
{
if (!_is_stop_threadpool)
{
task_t task = [&func, &self, args...]{ return (*self.*func)(args...); };
add_task_impl(task);
}
} void stop()
{
// 保证terminate_all函数只被调用一次
std::call_once(_call_flag, [this]{ terminate_all(); });
} private:
void add_task_impl(const task_t& task)
{
{
// 任务队列满了将等待线程池消费任务队列
std::unique_lock<std::mutex> locker(_task_queue_mutex);
while (_task_queue.size() == max_task_quque_size && !_is_stop_threadpool)
{
_task_put.wait(locker);
} _task_queue.emplace(std::move(task));
} // 向任务队列插入了一个任务并提示线程池可以来取任务了
_task_get.notify_one();
} void terminate_all()
{
_is_stop_threadpool = true;
_task_get.notify_all(); for (auto& iter : _thread_vec)
{
if (iter != nullptr)
{
if (iter->joinable())
{
iter->join();
}
}
}
_thread_vec.clear(); clean_task_queue();
} void run_task()
{
// 线程池循环取任务
while (true)
{
task_t task = nullptr;
{
// 任务队列为空将等待
std::unique_lock<std::mutex> locker(_task_queue_mutex);
while (_task_queue.empty() && !_is_stop_threadpool)
{
_task_get.wait(locker);
} if (_is_stop_threadpool)
{
break;
} if (!_task_queue.empty())
{
task = std::move(_task_queue.front());
_task_queue.pop();
}
} if (task != nullptr)
{
// 执行任务,并通知同步服务层可以向队列放任务了
task();
_task_put.notify_one();
}
}
} void clean_task_queue()
{
std::lock_guard<std::mutex> locker(_task_queue_mutex);
while (!_task_queue.empty())
{
_task_queue.pop();
}
} private:
std::vector<work_thread_ptr> _thread_vec;
std::condition_variable _task_put;
std::condition_variable _task_get;
std::mutex _task_queue_mutex;
std::queue<task_t> _task_queue;
std::atomic<bool> _is_stop_threadpool;
std::once_flag _call_flag;
}; #endif

测试代码

#include <iostream>
#include <string>
#include <chrono>
#include "thread_pool.hpp" void test_task(const std::string& str)
{
std::cout << "Current thread id: " << std::this_thread::get_id() << ", str: " << str << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} class Test
{
public:
void print(const std::string& str, int i)
{
std::cout << "Test: " << str << ", i: " << i << std::endl;
}
}; class Test2
{
public:
void operator()(const std::string& str, int i)
{
std::cout << "Test2: " << str << ", i: " << i << std::endl;
}
}; int main()
{
Test t;
Test2 t2;
thread_pool pool;
// 启动10个线程
pool.init_thread_num(10); std::string str = "Hello world"; for (int i = 0; i < 1000; ++i)
{
// 支持lambda表达式
pool.add_task([]{ std::cout << "Hello ThreadPool" << std::endl; });
// 支持全局函数
pool.add_task(test_task, str);
// 支持函数对象
pool.add_task(t2, str, i);
// 支持类成员函数
pool.add_task(&Test::print, &t, str, i);
} std::cin.get();
std::cout << "##############END###################" << std::endl;
return 0;
}

测试程序启动了十个线程并调用add_task函数加入了4000个任务,add_task支持普通全局函数、静态函数、类成员函数、函数对象(仿函数)以及lambda表达式,并且支持函数传入,该线程池的实现以及测试代码我已经放到了github上。

参考资料

《深入应用C++11--代码优化与工程级应用》

使用C++11实现一个半同步半异步线程池的更多相关文章

  1. 使用C++11 开发一个半同步半异步线程池

    摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...

  2. c++11 实现半同步半异步线程池

    感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...

  3. 分布式缓存系统 Memcached 半同步/半异步模式

    在前面工作线程初始化的分析中讲到Memcached采用典型的Master_Worker模式,也即半同步/半异步的高效网络并发模式.其中主线程(异步线程)负责接收客户端连接,然后分发给工作线程,具体由工 ...

  4. 领导者/追随者(Leader/Followers)模型和半同步/半异步(half-sync/half-async)模型都是常用的客户-服务器编程模型

    领导者-追随者(Leader/Followers)模型的比喻 半同步/半异步模型和领导者/追随者模型的区别: 半同步/半异步模型拥有一个显式的待处理事件队列,而领导者-追随者模型没有一个显式的队列(很 ...

  5. 半同步半异步模式的实现 - MSMQ实现

    半同步半异步模式的实现 - MSMQ实现 所谓半同步半异步是指,在某个方法调用中,有些代码行是同步执行方式,有些代码行是异步执行方式,下面我们来举个例子,还是以经典的PlaceOrder来说,哈哈. ...

  6. (原创)C++半同步半异步线程池2

    (原创)C++半同步半异步线程池 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 线程池可以高效的处理任务,线程池中开启多个线程,等待同步队列中的任务到来,任务到来多个线程 ...

  7. 【Networking】(转)一个非常好的epoll+线程池服务器Demo

    (转)一个非常好的epoll+线程池服务器Demo Reply 转载自:http://zhangyafeikimi.javaeye.com/blog/285193 [cpp] /** 张亚霏修改 文件 ...

  8. Half Sync And Half Async 半同步半异步模式

    如题,这是一个典型的CS结构的模式,至少曾经在CS结构中用过,只是没用好.当年用UDP死活都处理不过来网络命令,用此模式轻松解决. 此模式是典型的三层架构,同步层在一个线程内处理业务逻辑:异步层接受并 ...

  9. 一个ajax同步与异步引发的血案。

    前言 公司做网上促销活动,需要充值换取相应的抽奖资格,抽奖可以获得丰厚的礼品,而且抽奖资格门槛有点高,领导下达命令保证活动的正常上线与运行,领导很重视,就这样,在领导的安排下进行了相关活动的codin ...

随机推荐

  1. Codelf 搜索开源代码帮程序员命名

    "计算机科学里两件最难的事:缓存失效和命名." Codelf通过搜索在线开源平台Github, Bitbucket, Google Code, Codeplex, Sourcefo ...

  2. angular4 组件间通信

    父传子用@input 子传父用@output 例:子组件 <p> <span *ngFor="let star of stars;let i=index" cla ...

  3. Java23种设计模式学习笔记【目录总贴】

    创建型模式:关注对象的创建过程 1.单例​模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 主要: 饿汉式(线程安全,调用效率高,但是不能延时加载) 懒汉式(线程安全,调用效率不高,但 ...

  4. 苏宁易购微信端 wx ios android other 通过js来控制样式

    <!DOCTYPE HTML><html><head><meta charset="UTF-8"><meta name=&qu ...

  5. Python 名称空间与作用域、闭包与装饰器

    Python 的名称 Python 的名称(Name)是对象的一个标识(Identifier).我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的.说得有点玄乎,我们以例子说明. 例如 ...

  6. C#日期处理(转) 太忘记了,备忘

    //今天 DateTime.Now.Date.ToShortDateString(); //昨天,就是今天的日期减一 DateTime.Now.AddDays(-1).ToShortDateStrin ...

  7. Windows 下配置 php_imagick 扩展

    1.首先按装 imageimagick 可以去 http://imagemagick.org/script/binary-releases.php#windows 这里下载,看好自己的系统环境和选择好 ...

  8. 前端开发 - CSS - 总结

    CSS:层叠样式表(Cascading Style Sheets) 1.css的特征2.css的引入3.选择器4.伪类选择器5.伪元素选择器6.字体样式 文本样式 背景属性7.盒模型 border m ...

  9. 自定义HTTP头时的注意事项(转)

    原文:https://blog.gnuers.org/?p=462 HTTP头是可以包含英文字母([A-Za-z]).数字([0-9]).连接号(-)hyphens, 也可义是下划线(_).在使用ng ...

  10. 《iOS Human Interface Guidelines》——Popover

    弹出框 弹出框是当人们点击一个控件或屏幕上一个区域时显示的一个暂时的界面. API NOTE 在iOS 8及以后的系统中.你能够使用UIPopoverPresentationController来显示 ...