[9 使用C++11开发一个半同步半异步线程池]

9.1 半同步半异步线程池介绍

传统的一个请求一个线程处理,在处理大量并发任务时,会导致大量的线程创建和销毁,消耗过多的系统资源。线程池可以解决这个问题。

本章介绍的是半同步半异步线程池,线程池结构图如图9-1:

图9-1 半同步半异步线程池

第一层是同步服务层,它处理来自上层的任务请求。上层的请求可能是并发的,这些请求不是马上被处理,而是将这些请求放到一个同步队列里。

第二层是排队层,即同步队列。

第三层是异步服务层,多个线程从同步队列里取出任务并处理。

9.2 线程池实现的关键技术分析

线程池有两个活动过程,一个是向同步队列里添加任务的过程,另一个是从同步队列里取任务的过程,活动图如图9-2:

 图9-2 半同步半异步线程池活动图

注意,同步队列会限制任务数的上限,避免任务过多导致内存暴涨。

9.3 同步队列

同步队列的实现代码:

#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;

template<typename T>
class SyncQueue
{
public:
    SyncQueue(int maxSize) : m_maxSize(maxSize), m_needStop(false)
    {
    }

    void Put(const T& x)
    {
        Add(x);
    }

    void Put(T&& x)
    {
        Add(std::forward<T>(x));
    }

    void Take(std::list<T>& list)
    {
        std::unique_lock<std::mutex> locker(m_mutex);
        // return条件都不满足,则wait
        m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});

        if(m_needStop) return;
        // std::move后,m_queue为空!
        list = std::move(m_queue);
        m_notFull.notify_one();
    }

    void Take(T& t)
    {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});

        if(m_needStop) return;
        t = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();
    }

    void Stop()
    {
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            m_needStop = true;
        }
        m_notFull.notify_all();
        m_notEmpty.notify_all();
    }    

    bool Empty()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.empty();
    }

    bool Full()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size() == m_maxSize;
    }

    size_t Size()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size();
    }

private:
    bool NotFull() const
    {
        bool full = m_queue.size() >= m_maxSize;
        if(full) cout << "缓冲区满了,需要等待..." << endl;
        return !full;
    }

    bool NotEmpty() const
    {
        bool empty = m_queue.empty();
        if(empty) cout << "缓冲区空了,需要等待..." << endl;
        return !empty;
    }

    template<typename F>
    void Add(F&& x)
    {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notFull.wait(locker, [this]{return m_needStop || NotFull();});

        if(m_needStop) return;
        m_queue.push_back(std::forward<F>x);
        m_notEmpty.notify_one();
    }

private:
    std::list<T> m_queue;    //缓冲区
    std::mutex  m_mutex;
    std::condition_variable m_notEmpty;    //缓冲区不为空的条件变量
    std::condition_variable m_notFull;     //缓冲区不满的条件变量
    int m_maxSize;
    bool m_needStop;
};

Take函数

void Take(std::list<T>& list)

每个数据加锁效率较低,这里改进,一次加锁就能将队列中所有数据取出,大大减少了加锁次数。

m_notEmpy.wait(locker, [this]{ return m_needStop || NotEmpty(); })

判断式由两个条件组成,一个是停止的标志,一个是不为空的条件。当不满足任何一个条件时,条件变量会释放mutex并将线程置于wait状态,等待其他线程notify_one或notify_all将其唤醒。

9.4 线程池

线程池的实现:

#include <list>
#include <thread>
#include <functional>
#include <memory>
#include <atomic>
#include "SyncQueue.hpp"

const int MaxTaskCount = 100;
class ThreadPool
{
public:
    using Task = std::function<void(void)>;
    
    ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount)
    {
        Start(numThreads);
    }
    
    ~ThreadPool()
    {
        // 如果没有停止,则主动停止线程池
        Stop();
    }

    void Stop()
    {
        // 保证多线程下只调用一次StopThreadGroup
        std::call_once(m_flag, [this]{ StopThreadGroup(); });
    }

    void AddTask(Task&& task)
    {
        m_queue.Put(std::forward<Task>(task));
    }

    void AddTask(const Task& task)
    {
        m_queue.Put(task);
    }

private:
    void Start(int numThreads)
    {
        m_running = true;
        // 创建线程组
        for (int i = 0;i < numThreads; i++)
        {
            // thread runs ThreadPool::RunInThread() on object this
            m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
        }
    }

    void RunInThread()
    {
        while (m_running) 
        {
            std::list<Task> list;
            m_queue.Take(list);
            
            for(auto& task:list)
            {
                if(!m_running) return;
                cout << "异步服务层线程id:" << thdId << endl;
                task();
            }
        }
    }

    void StopThreadGroup()
    {
        m_queue.Stop();
        m_running = false;
        
        for (auto thread : m_threadgroup)
        {
            if(thread) thread->join();
        }
        m_threadgroup.clear();
    }

    // 处理任务的线程组
    std::list<std::shared_ptr<std::thread>> m_threadgroup;
    // 同步队列
    SyncQueue<Task> m_queue;
    atomic_bool m_running;
    std::once_flag m_flag;
};

9.5 应用实例

void TestPool()
{
    ThreadPool pool;

    for (int i = 0; i < 10; i++)
    {
        auto thdId = this_thread::get_id();
        pool.AddTask([thdId]{
            cout << "同步服务层线程id:" << thdId << endl;
        });
    }

    this_thread::sleep_for(std::chrono::seconds(2));
    pool.Stop();
}

线程池会根据当前cpu个数创建对应个数的异步线程。同步服务层线程不断地向同步队列添加任务,线程池里的线程会并行处理。

使用线程池时注意:

(1)要保证线程池中的任务不能挂死,否则会耗尽线程池中的线程。

(2)要避免长时间执行一个任务,会导致后面的任务大量堆积而得不到及时处理。对于耗时较长的任务建议采用单个线程处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于C++11标准的多线程、同步机制、线程池异步编程技术实现的多客户并发访问和模拟多个用户进行压力测试的服务端和客户端代码。其中,使用了Boost库的线程池实现。 服务端代码: ```cpp #include <iostream> #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/shared_ptr.hpp> #include <boost/asio/thread_pool.hpp> using boost::asio::ip::tcp; class session : public boost::enable_shared_from_this<session> { public: session(boost::asio::io_service& io_service) : socket_(io_service) { } tcp::socket& socket() { return socket_; } void start() { boost::asio::async_read(socket_, boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void handle_read(const boost::system::error_code& error, size_t bytes_transferred) { if (!error) { boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), boost::bind(&session::handle_write, shared_from_this(), boost::asio::placeholders::error)); } else { std::cout << "Error: " << error.message() << std::endl; } } void handle_write(const boost::system::error_code& error) { if (!error) { boost::asio::async_read(socket_, boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } else { std::cout << "Error: " << error.message() << std::endl; } } private: tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; }; class server { public: server(boost::asio::io_service& io_service, short port) : io_service_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)) { start_accept(); } void start_accept() { session_ptr new_session(new session(io_service_)); acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error)); } void handle_accept(session_ptr session, const boost::system::error_code& error) { if (!error) { session->start(); start_accept(); } else { std::cout << "Error: " << error.message() << std::endl; } } private: boost::asio::io_service& io_service_; tcp::acceptor acceptor_; }; int main(int argc, char* argv[]) { try { if (argc != 2) { std::cerr << "Usage: server <port>\n"; return 1; } boost::asio::io_service io_service; server s(io_service, std::atoi(argv[1])); boost::asio::thread_pool thread_pool(4); boost::asio::post(thread_pool, [&io_service]() { io_service.run(); }); boost::asio::post(thread_pool, [&io_service]() { io_service.run(); }); boost::asio::post(thread_pool, [&io_service]() { io_service.run(); }); boost::asio::post(thread_pool, [&io_service]() { io_service.run(); }); thread_pool.join(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; } ``` 客户端代码: ```cpp #include <iostream> #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/asio/thread_pool.hpp> using boost::asio::ip::tcp; class client { public: client(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator) : io_service_(io_service), socket_(io_service) { boost::asio::async_connect(socket_, endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error)); } void handle_connect(const boost::system::error_code& error) { if (!error) { boost::asio::async_write(socket_, boost::asio::buffer(request_, request_length), boost::bind(&client::handle_write, this, boost::asio::placeholders::error)); } else { std::cout << "Error: " << error.message() << std::endl; } } void handle_write(const boost::system::error_code& error) { if (!error) { boost::asio::async_read(socket_, boost::asio::buffer(reply_, reply_length), boost::bind(&client::handle_read, this, boost::asio::placeholders::error)); } else { std::cout << "Error: " << error.message() << std::endl; } } void handle_read(const boost::system::error_code& error) { if (!error) { boost::asio::async_write(socket_, boost::asio::buffer(request_, request_length), boost::bind(&client::handle_write, this, boost::asio::placeholders::error)); } else { std::cout << "Error: " << error.message() << std::endl; } } private: boost::asio::io_service& io_service_; tcp::socket socket_; enum { request_length = 1024, reply_length = 8 }; char request_[request_length]; char reply_[reply_length]; }; int main(int argc, char* argv[]) { try { if (argc != 3) { std::cerr << "Usage: client <host> <port>\n"; return 1; } boost::asio::io_service io_service; tcp::resolver resolver(io_service); auto endpoint_iterator = resolver.resolve({ argv[1], argv[2] }); const int num_clients = 100; boost::asio::thread_pool thread_pool(num_clients); for (int i = 0; i < num_clients; ++i) { boost::asio::post(thread_pool, [&io_service, &endpoint_iterator]() { client c(io_service, endpoint_iterator); }); } thread_pool.join(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; } ``` 这里的服务端使用异步I/O模型,每个客户端连接对应一个session对象,处理客户端请求时使用异步读写操作。同时,为了提高并发处理能力,使用了Boost库的线程池实现多线程处理客户端请求。 客户端使用异步I/O模型,每个客户端连接使用一个client对象,处理客户端请求时使用异步读写操作。为了模拟多个用户进行压力测试,使用了Boost库的线程池实现多线程创建客户端连接。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值