手撕代码 C++11线程池 极简80行代码

好几篇面试的文章看到要手撕线程池和消费者、生产者的所以先准备个答案

生产者消费者

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

// 生产者消费者

template <class T>
class Queue {
public:
    void put(T val) {
        unique_lock<mutex> _lock(mtx);
        while (!que.empty()) {
            cv.wait(_lock);
        }
        que.push(val);
        cv.notify_all();
        cout << "生产 "<< val << endl;
    }

    T get() {
        unique_lock<mutex> _lock(mtx);
        while (que.empty()) {
            cv.wait(_lock);
        }
        T ret = que.front();
        que.pop();
        cv.notify_all();
        cout << "消费 "<< ret << endl;
        return ret;
    }

private:
    mutex mtx;
    condition_variable cv;
    queue<T> que;
};

int main() {
    cout << "begin" << endl;
    Queue<int> q;

    std::thread t1([&q]()->void { // 生产者线程
            for (int i = 1; i <= 10; ++i)
	        {
		        q.put(i);
		        std::this_thread::sleep_for(std::chrono::milliseconds(100));
	        } 
        });
	std::thread t2([&q]()->void { // 消费者线程
            for (int i = 1; i <= 10; ++i)
	        {
		        q.get();
		        std::this_thread::sleep_for(std::chrono::milliseconds(100));
	        }
        });

	t1.join();
	t2.join();
    cout << "end" << endl;
    return 0;
}

线程池

原github 链接
https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h
设计非常精巧,其次这里可以把bool 换成 原子类型,可用节省一次lock调用,原子类型开销会低一点

其次用到了future 和 packaged_task封装返回值,不太懂的可以看看具体的使用例子,这里放个链接

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

class ThreadPool {
public:
    ThreadPool(size_t);
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) 
        -> std::future<typename std::result_of<F(Args...)>::type>;
    ~ThreadPool();
private:
    // need to keep track of threads so we can join them
    std::vector< std::thread > workers;
    // the task queue
    std::queue< std::function<void()> > tasks;
    
    // synchronization
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};
 
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) // 消费者线程
    :   stop(false)
{
    for(size_t i = 0;i<threads;++i)
        workers.emplace_back( // 将lambda放入vector里构造thread,用到是emplace,直接在容器内部构造
            [this]
            {
                for(;;)
                {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });
                        if(this->stop && this->tasks.empty()) // 停止前还会将队列消费完
                            return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }

                    task();
                }
            }
        );
}

// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)  // 生产者线程
    -> std::future<typename std::result_of<F(Args...)>::type>
{
    using return_type = typename std::result_of<F(Args...)>::type;

    auto task = std::make_shared< std::packaged_task<return_type()> >(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);

        // don't allow enqueueing after stopping the pool
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task](){ (*task)(); });
    }
    condition.notify_one();
    return res;
}

// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all(); // 唤醒所有线程
    for(std::thread &worker: workers)
        worker.join(); // 回收线程
}

#endif

简化后的线程池

删掉了future 和 packaged_task,即略去了返回值,面试手撕更容易些。

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>

class ThreadPool {
public:
    ThreadPool(size_t);
    template<class F, class... Args>
    void enqueue(F&& f, Args&&... args);
    ~ThreadPool();
private:
    // need to keep track of threads so we can join them
    std::vector< std::thread > workers;
    // the task queue
    std::queue< std::function<void()> > tasks;
    // synchronization
    std::mutex queue_mutex;
    std::condition_variable condition;
    std::atomic<bool> stop;
};
 
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) // 消费者线程
    :   stop(false)
{
    for(size_t i = 0; i < threads; ++i)
        workers.emplace_back( // 将lambda放入vector里构造thread,用到是emplace,直接在容器内部构造
            [this]
            {
                for(;;)
                {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });
                        if(this->stop && this->tasks.empty()) // 停止前还会将队列消费完
                            return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }

                    task();
                }
            }
        );
}

// add new work item to the pool
template<class F, class... Args>
void ThreadPool::enqueue(F&& f, Args&&... args)  // 生产者线程
{   
    auto task =  std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    {
        std::unique_lock<std::mutex> lock(queue_mutex);

        // don't allow enqueueing after stopping the pool
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task](){ (task)(); });
    }
    condition.notify_one();
}

// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
    stop = true;
    condition.notify_all(); // 唤醒所有线程
    for(std::thread &worker: workers)
        worker.join(); // 回收线程
}
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言线程池代码实现: ```c #include <pthread.h> #include <stdlib.h> #include <stdio.h> #define DEFAULT_THREADS 4 typedef struct { void (*function)(void *); void *argument; } thread_task; typedef struct { thread_task *task_queue; int queue_size; int front; int rear; int count; pthread_mutex_t lock; pthread_cond_t not_empty; pthread_cond_t not_full; int threads_count; pthread_t *threads; int shutdown; } thread_pool; void *thread_pool_function(void *thread_pool_ptr) { thread_pool *pool = (thread_pool *) thread_pool_ptr; thread_task task; while (1) { pthread_mutex_lock(&(pool->lock)); while (pool->count == 0 && !pool->shutdown) { pthread_cond_wait(&(pool->not_empty), &(pool->lock)); } if (pool->shutdown) { pthread_mutex_unlock(&(pool->lock)); pthread_exit(NULL); } task = pool->task_queue[pool->front]; pool->front = (pool->front + 1) % pool->queue_size; pool->count--; if (pool->count == pool->queue_size - 1) { pthread_cond_broadcast(&(pool->not_full)); } pthread_mutex_unlock(&(pool->lock)); (*(task.function))(task.argument); } pthread_exit(NULL); } int thread_pool_create(thread_pool *pool, int threads_count) { int i; pool->threads_count = 0; pool->shutdown = 0; if (threads_count <= 0) { threads_count = DEFAULT_THREADS; } pool->queue_size = threads_count * 2; pool->front = 0; pool->rear = 0; pool->count = 0; pool->task_queue = (thread_task *) malloc(sizeof(thread_task) * pool->queue_size); pool->threads = (pthread_t *) malloc(sizeof(pthread_t) * threads_count); pthread_mutex_init(&(pool->lock), NULL); pthread_cond_init(&(pool->not_empty), NULL); pthread_cond_init(&(pool->not_full), NULL); for (i = 0; i < threads_count; i++) { pthread_create(&(pool->threads[i]), NULL, thread_pool_function, (void *) pool); pool->threads_count++; } return 0; } int thread_pool_add_task(thread_pool *pool, void (*function)(void *), void *argument) { thread_task task; pthread_mutex_lock(&(pool->lock)); while (pool->count == pool->queue_size && !pool->shutdown) { pthread_cond_wait(&(pool->not_full), &(pool->lock)); } if (pool->shutdown) { pthread_mutex_unlock(&(pool->lock)); return -1; } task.function = function; task.argument = argument; pool->task_queue[pool->rear] = task; pool->rear = (pool->rear + 1) % pool->queue_size; pool->count++; if (pool->count == 1) { pthread_cond_broadcast(&(pool->not_empty)); } pthread_mutex_unlock(&(pool->lock)); return 0; } int thread_pool_destroy(thread_pool *pool) { int i; if (pool->shutdown) { return -1; } pool->shutdown = 1; pthread_cond_broadcast(&(pool->not_empty)); pthread_cond_broadcast(&(pool->not_full)); for (i = 0; i < pool->threads_count; i++) { pthread_join(pool->threads[i], NULL); } free(pool->task_queue); free(pool->threads); pthread_mutex_destroy(&(pool->lock)); pthread_cond_destroy(&(pool->not_empty)); pthread_cond_destroy(&(pool->not_full)); return 0; } void task_function(void *argument) { int *value = (int *) argument; printf("Task %d\n", *value); } int main() { int i; int values[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; thread_pool pool; thread_pool_create(&pool, 4); for (i = 0; i < 10; i++) { thread_pool_add_task(&pool, task_function, &values[i]); } thread_pool_destroy(&pool); return 0; } ``` 该实现使用一个循环队列来存储线程任务,线程池的每个线程都会不断地从队列中取出任务并执线程池的创建、添加任务和销毁都是线程安全的,确保了多线程环境下线程池的正确性。在本例中,我们创建了一个包含10个任务的线程池,每个任务打印一个数字。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值