C++实现简单线程池

一些C++11特性

了解一下lambda表达式,利用Lambda表达式,可以方便的定义和创建匿名函数

值捕获

int main()
{
    int a = 123;
    auto f = [a] { cout << a << endl; }; 
    a = 321;
    f(); // 输出:123
}

引用捕获

int main()
{
    int a = 123;
    auto f = [&a] { cout << a << endl; }; 
    a = 321;
    f(); // 输出:321
}

隐式捕获

int main()
{
    int a = 123;
    auto f = [=] { cout << a << endl; };    // 值捕获
    f(); // 输出:123
}

int main()
{
    int a = 123;
    auto f = [&] { cout << a << endl; };    // 引用捕获
    a = 321;
    f(); // 输出:321
}

类型尾置
让编译器在函数定义的时候知道返回类型

template 
auto &getItem(T begin, T end) -> decltype(*begin) {
    return *begin; // 返回序列中一个元素的引用
}

线程相关操作

#include 
#include 
void foo() {
    std::cout << "hello world" << std::endl;
}
int main() {
    std::thread t(foo);//创建一个线程实例
    t.join();//加入一个线程
    return 0;
}


std::mutex mutex  创建一个互斥量
std::lock_guard lock(mutex); 对互斥量上锁
std::unique_lock   也是上锁,但更灵活
std::packaged_task task([](){return 7;});  用来封装任何可以调用的目标,从而用于实现异步的调用,异步即主线程A想获取某个计算结果而调用线程B
std::future result = task.get_future();   用来获取异步任务的结果
std::thread(std::move(task)).detach();   一个线程中执行 task
std::this_thread::sleep_for   当前线程休眠一段时间,休眠期间不与其他线程竞争CPU,根据线程需求,等待若干时间
std::condition_variable   唤醒等待线程从而避免死锁
std::bind  将实参绑定到调用函数上
std::placeholders::_1  占用符
std::shared_ptr   一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象
std::make_shared   分配创建传入参数中的对象,并返回这个对象类型的std::shared_ptr指针
std::move  将自己的参数转换为右值
std::forward 会把参数被绑定到一个右值的时候将其转化为右值
std::result_of 在编译的时候推导出一个函数调用表达式的返回值类型

看一个操作系统中生产者与消费者问题

假设存在一个缓冲区,生产者往里面存数据,消费者从里面取数据,如果缓冲区满了,生产者就不能再往里面添加数据。如果缓冲区没有数据,消费者不能从里面取

下面利用c++11来写一个简单的模型,要理解多线程并发,一个程序可能由多个线程来执行,因此程序上的顺序并不同于多线程中的执行顺序,代码中先写5个生产者,再写5个消费者,但是线程中的顺序并不是这样,可能先执行一个生产者,再执行一个消费者。

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    // 生产者数量
    std::queue produced_nums;
    // 互斥锁
    std::mutex m;
    // 条件变量
    std::condition_variable cond_var;
    // 结束标志
    bool done = false;
    // 通知标志
    bool notified = false;

    // 生产者线程
    std::thread producer([&]() {
        for (int i = 0; i < 5; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));//当前线程休眠
            // 创建互斥锁
            std::unique_lock lock(m);
            std::cout << "producing " << i << '\n';
            produced_nums.push(i);
            notified = true;
            // 通知一个线程
            cond_var.notify_one();
        }
        done = true;//生产结束
        cond_var.notify_one();//通知休眠的线程执行,完成所有的进程
    });

    // 消费者线程
    std::thread consumer([&]() {
        std::unique_lock lock(m);
        while (!done) {
            while (!notified) {  // 循环避免虚假唤醒,执行完生产者的进程,notified为true,否则停掉消费者线程
                cond_var.wait(lock);//停掉当前线程

            }
            while (!produced_nums.empty()) {
                std::cout << "consuming " << produced_nums.front() << '\n';
                produced_nums.pop();
            }
            notified = false;
        }
    });
    producer.join();
    consumer.join();
}
#ifndef ThreadPool_hpp
#define ThreadPool_hpp
#include                // std::vector
#include                 // std::queue
#include                // std::make_shared
#include             // std::runtime_error
#include                // std::thread
#include                 // std::mutex,        std::unique_lock
#include    // std::condition_variable
#include                // std::future,       std::packaged_task
#include            // std::function,     std::bind
#include               // std::move,         std::forward

class ThreadPool {
public:
    inline ThreadPool(size_t threads) : stop(false) { //构造函数,且把stop变量赋值为false
        for(size_t i = 0;i task;//function函数对象类,可调用实体的一种类型安全的包裹
                    {
                        std::unique_lock lock(this->queue_mutex);//互斥量上锁
                        //std::cout<<"thread"<condition.wait(lock,[this]{ return this->stop || !this->tasks.empty(); });//如果线程池没有销毁且任务队列为空,返回false,该线程休眠
                        if(this->stop && this->tasks.empty())//线程池销毁且任务队列为空,返回
                            return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();

                    }
                    task();//执行任务
                }

                std::cout<<"thread"< lock(queue_mutex);//互斥量上锁,避免
            stop = true;
        }
        condition.notify_all();//通知所有的休眠线程
        for(std::thread &worker: workers)
            worker.join();
    }
    template
    auto enqueue(F&& f, Args&&... args)//可变参数的模板,Args 是一个模板参数包。而在后面的函数参数表中,args 则是函数参数包,用来表示零个或多个参数。
    -> std::future::type> {
        using return_type = typename std::result_of::type;//获取函数返回类型
        auto task = std::make_shared< std::packaged_task>(
                std::bind(std::forward(f), std::forward(args)...)
        );
        std::future res = task->get_future();//获得 std::future 对象以供实施线程同步
        {
            std::unique_lock lock(queue_mutex);

            if(stop)
                throw std::runtime_error("enqueue on stopped ThreadPool");
            tasks.emplace([task]{ (*task)(); });//把任务加入队列
        }
        condition.notify_one();//唤醒一个休眠的线程
        return res;
    }
private:
    std::vector workers;//线程池
    std::queue> tasks;//任务队列
    std::mutex queue_mutex;//互斥量
    std::condition_variable condition;//条件变量
    bool stop;//结束标志
};
#endif /* ThreadPool_hpp */

伪代码:

semaphore mutex=1; //临界区互斥信号量
semaphore empty=n;  //空闲缓冲区
semaphore full=0;  //缓冲区初始化为空
producer ()//生产者进程 
{
    while(1)
    {
        produce an item in nextp;  //生产数据
        P(empty);  //获取空缓冲区单元
        P(mutex);  //进入临界区.
        add nextp to buffer;  //将数据放入缓冲区
        V(mutex);  //离开临界区,释放互斥信号量
        V(full);  //满缓冲区数加1
    }
}

consumer ()//消费者进程
{
    while(1)
    {
        P(full);  //获取满缓冲区单元
        P(mutex);  // 进入临界区
        remove an item from buffer;  //从缓冲区中取出数据
        V (mutex);  //离开临界区,释放互斥信号量
        V (empty) ;  //空缓冲区数加1
        consume the item;  //消费数据
    }
}

你可能感兴趣的:(C++实现简单线程池)