在多处理器普及的现在,服务器程序会充分使用计算机多核的能力,比如nginx的master-worker多进程模式,那么今天我们就在1的基础上来通过多线程来使用多核。
进程和线程数不是越多越好,一般不要超过CPU核数的2倍。io_service是asio的事件循环,那么多线程就是几个io_service在各自的线程运行,这里我们使用io_service_pool类管理他们。
#define CPUS sysconf(_SC_NPROCESSORS_ONLN)
class io_service_pool : private boost::noncopyable
{
public:
typedef boost::shared_ptr<io_service> io_service_ptr;
typedef boost::shared_ptr<io_service::work> work_ptr;
io_service_pool()
: m_index(0)
{
std::cout << "--init--" << std::endl;
init();
std::cout << "--init--" << std::endl;
}
~io_service_pool()
{
}
void start()
{
std::cout << "--start--" << std::endl;
if (!m_threads.size()) {
for (std::vector<io_service_ptr>::iterator ite = m_io_services.begin();
ite != m_io_services.end(); ++ite) {
m_threads.create_thread(boost::bind(&io_service::run, (*ite).get()));
}
std::cout << "--join--" << std::endl;
m_threads.join_all();
std::cout << "--join--" << std::endl;
}
std::cout << "--start--" << std::endl;
}
void stop()
{
for (std::vector<io_service_ptr>::iterator ite = m_io_services.begin();
ite != m_io_services.end(); ++ite) {
(*ite)->post(boost::bind(&io_service::stop, (*ite).get()));
}
m_works.clear();
}
io_service_ptr get()
{
if (m_index >= m_io_services.size()) {
m_index = 0;
}
return m_io_services[m_index++];
}
private:
void init(int cpu = CPUS)
{
std::cout << "--cpu--:" << cpu << std::endl;
for (int i = 0; i < cpu; ++i) {
m_io_services.push_back(io_service_ptr(new io_service));
m_works.push_back(work_ptr(new io_service::work(*m_io_services.back())));
}
}
int m_index;
std::vector<io_service_ptr> m_io_services;
std::vector<work_ptr> m_works;
boost::thread_group m_threads;
};
多线程方面我们使用boost::thread_group
,其用来管理线程池很方便。
init
函数根据本机的cpu核心数来初始化几个io_service,为了防止io_service在运行过程中因为如果没有需要等待的异步任务而退出,我们这个使用了io_service::work类,每个work类与和一个io_service绑定,有一个work就会改变io_service的中的一个标示+1,当其不为0的时候,io_service是不会退出循环的。
void start()
{
std::cout << "--start--" << std::endl;
if (!m_threads.size()) {
for (std::vector<io_service_ptr>::iterator ite = m_io_services.begin();
ite != m_io_services.end(); ++ite) {
m_threads.create_thread(boost::bind(&io_service::run, (*ite).get()));
}
std::cout << "--join--" << std::endl;
m_threads.join_all();
std::cout << "--join--" << std::endl;
}
std::cout << "--start--" << std::endl;
}
start
函数根据io_service来起线程,然后相应的线程中在执行run方法来跑事件循环,最后调用join_all
使主线程等待所有线程结束。
我们使用轮询的策略来分配连接进来的客户连接。
io_service_ptr get()
{
if (m_index >= m_io_services.size()) {
m_index = 0;
}
return m_io_services[m_index++];
}
echo2服务器类
class TestEchoServer2 : public boost::enable_shared_from_this<TestEchoServer2>
, private boost::noncopyable
{
public:
TestEchoServer2(io_service_pool &service_pool, int port)
: m_io_service_pool(service_pool)
, m_endpoint(ip::tcp::v4(), port)
, m_acceptor(*(m_io_service_pool.get()), m_endpoint)
{
}
~TestEchoServer2()
{
}
void start_accept()
{
m_connect.reset(new TestEchoConnection(*(m_io_service_pool.get()), m_connectionManager));
m_acceptor.async_accept(m_connect->socket(), boost::bind(&TestEchoServer2::handle_accept, shared_from_this(), _1));
}
private:
void handle_accept(const boost::system::error_code &err)
{
if (!err) {
m_connectionManager.push_back(m_connect);
m_connect->start();
}
start_accept();
}
private:
io_service_pool& m_io_service_pool;
ip::tcp::endpoint m_endpoint;
ip::tcp::acceptor m_acceptor;
MutexConnectionManager m_connectionManager;
TestEchoConnection_ptr m_connect;
};
不同之处:
void start_accept()
{
m_connect.reset(new TestEchoConnection(*(m_io_service_pool.get()), m_connectionManager));
m_acceptor.async_accept(m_connect->socket(), boost::bind(&TestEchoServer2::handle_accept, shared_from_this(), _1));
}
将新的connect绑定时是从线程池中获取的。
开始我没有修改connectionManager
,还是使用的std::list,但是它不是线程安全的,我也没有加锁,然后跑跑就挂了。然后给他加上了锁。
template <typename T, typename T2>
class MutexList {
public:
MutexList()
{
}
~MutexList()
{
}
void remove(const T2& value)
{
m_mutex.lock();
m_t2s.remove(value);
m_mutex.unlock();
}
void push_back(const T2& value)
{
m_mutex.lock();
m_t2s.push_back(value);
m_mutex.unlock();
}
private:
std::list<T2> m_t2s;
T m_mutex;
};
typedef MutexList<boost::mutex, TestEchoConnection_ptr> MutexConnectionManager;
那么对Connect也做了点修改,就是connectmanager的声明,操作的接口都是一样的,所以修改还是比较小。但是引入了锁竞争,而且那么对线程的优势在接入和断开这里就没了,比较好的解决办法就是没有线程使用自己各自的connectmanager。
使用siege测试
Transactions: 3799 hits
Availability: 100.00 %
Elapsed time: 19.81 secs
Data transferred: 3.20 MB
Response time: 0.00 secs
Transaction rate: 191.77 trans/sec
Throughput: 0.16 MB/sec
Concurrency: 0.26
Successful transactions: 3799
Failed transactions: 0
Longest transaction: 0.04
Shortest transaction: 0.00
性能还下降了,可能机器不一样有关,但是我感觉更多的是锁。