[C++]使用c++20信号量以及Impl模式的线程池

使用C++20的信号量来替代条件变量,
使用 impl 模式来隐藏具体的实现,可减少编译时间以及隐藏具体的细节实现。

PS:

  1. 任务队列如果使用无锁队列的话效率应该会有所提高。
  2. Impl模式可以隐藏数据成员以及实现,但是由于C++编译时需要确定类型的大小,所以使用智能指针。

头文件(接口)

ThreadPool.h:

#pragma once

#include <cstddef>
#include <memory>
#include <functional>

class ThreadPool {
private:
    void _enqueue(std::function<void()> task);

public:
    ThreadPool(const std::size_t threadnum);
    ~ThreadPool();
    template <typename Func, typename...Args> 
	inline void enqueue(Func &&f, Args&&...args) noexcept {
        return _enqueue(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
	}
private:
    // 使用 impl 来隐藏具体的实现,可减少编译时间以及隐藏具体的细节实现
    class Impl; 
    std::unique_ptr<Impl> m_handle;
};

源文件(实现)

ThreadPool.cpp:

#include <cstddef>
#include <atomic>
#include <memory>
#include <semaphore>
#include <vector>
#include <thread>
#include <mutex>

#include "ThreadPool.h"

constexpr unsigned EACH_THREAD_NUMS { 512 };

class ThreadPool::Impl {
    class Tasks;
private:
    std::unique_ptr<Tasks> m_tasks;
    // 用于管理空闲槽位
    std::counting_semaphore<> m_free_slots;	
    // 用于管理可用任务
    std::counting_semaphore<> m_available_tasks{0};	
    // 工作线程
	std::vector<std::thread> m_workers;		
    // 是否停止					
	std::atomic<bool> m_stop{false};							
private:
    void work_func();
public:
    Impl(const unsigned thread_nums);
    ~Impl();
    void enqueue(std::function<void()> &&task);
};

class ThreadPool::Impl::Tasks {
private:
    const unsigned m_nums;
	std::vector<std::function<void()>> m_tasks;
    unsigned m_start{0};  
	unsigned m_end{0};   
	unsigned m_count{0};
	std::mutex m_lock;	
public:
    Tasks(const unsigned nums);
    ~Tasks() = default;
    bool push(std::function<void()> &&in);
    bool pop(std::function<void()> &out);
};

ThreadPool::ThreadPool(const unsigned threadnum)
    : m_handle(std::make_unique<Impl>(threadnum)) {
    ;
}

ThreadPool::~ThreadPool() {
    ;
}

void ThreadPool::_enqueue(std::function<void()> task) {
    return m_handle->enqueue(std::move(task));
}

void ThreadPool::Impl::work_func() {
    while (true) {
        std::function<void()> task;
        m_available_tasks.acquire(); // 等待有任务
        if (!m_tasks->pop(task)) {
            if (m_stop) {
                return;
            }
            continue;
        }
        m_free_slots.release();
        if (task) {
            task();
        }
    }
}

ThreadPool::Impl::Impl(const unsigned thread_nums)
    : m_tasks(std::make_unique<Tasks>(thread_nums * EACH_THREAD_NUMS)), m_free_slots(thread_nums * EACH_THREAD_NUMS) {
    for (unsigned i { 0 }; i < thread_nums; ++i) {
        m_workers.emplace_back(&ThreadPool::Impl::work_func, this);
    }
}

ThreadPool::Impl::~Impl() {
    m_stop = true;
    // +256是为了防止虚假唤醒
    for (unsigned i = 0; i < m_workers.size() + 256; ++i) {
        m_available_tasks.release();
    }
    for (std::thread &worker : m_workers) {
        if (worker.joinable()) {
            worker.join();
        }
    }
}

void ThreadPool::Impl::enqueue(std::function<void()> &&task) {
    if (m_stop) {
        return;
    }
    m_free_slots.acquire();
    if (!m_tasks->push(std::move(task))) {
        return;
    }
    m_available_tasks.release();
}

ThreadPool::Impl::Tasks::Tasks(const unsigned nums)
    : m_nums(nums) {
    m_tasks.resize(nums);
}

bool ThreadPool::Impl::Tasks::push(std::function<void()> &&in) {
    std::lock_guard<std::mutex> lock(m_lock);
    if (m_nums == m_count) {
        return false;
    }
    m_tasks[m_end] = std::move(in);
    m_end = (m_end + 1) % m_nums;
    ++m_count;
    return true;
}

bool ThreadPool::Impl::Tasks::pop(std::function<void()> &out) {
    std::lock_guard<std::mutex> lock(m_lock);
    if (0 == m_count) {
        return false;
    }
    out = std::move(m_tasks[m_start]);
    m_start = (m_start + 1) % m_nums;
    --m_count;
    return true;
}

测试案例

test.cpp:

#include <chrono>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <thread>
#include <atomic>

#include "ThreadPool.h"

static std::atomic<std::uint64_t> s_count;

void test_threadfunc(const int idx) {
	const std::size_t count{ s_count++ };
	for (int i { 0 }; i < 10; ++i) {
		// std::printf("[%llu]: Hello\n", count);
	}
	// std::printf("[%llu]: Hello\n", count);
}

int main() {
	try {
		const size_t threadCount = 3000; // 线程数量
		do {
			ThreadPool pool(threadCount);
			// 提交一些任务
			for (int i { 0 }; i < 65536; ++i) {
				pool.enqueue(test_threadfunc, i);
			}

			std::this_thread::sleep_for(std::chrono::seconds(5));
		} while (0);
		std::cout << "count: " << s_count << std::endl;
	}
	catch (const std::exception &e) {
		std::cout << "Exception: " << e.what() << std::endl;
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值