一些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 <typename T>
auto &getItem(T begin, T end) -> decltype(*begin) {
return *begin; // 返回序列中一个元素的引用
}
线程相关操作
#include <iostream>
#include <thread>
void foo() {
std::cout << "hello world" << std::endl;
}
int main() {
std::thread t(foo);//创建一个线程实例
t.join();//加入一个线程
return 0;
}
std::mutex mutex 创建一个互斥量
std::lock_guard<std::mutex> lock(mutex); 对互斥量上锁
std::unique_lock 也是上锁,但更灵活
std::packaged_task<int()> task([](){return 7;}); 用来封装任何可以调用的目标,从而用于实现异步的调用,异步即主线程A想获取某个计算结果而调用线程B
std::future<int> 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 <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
#include <chrono>
int main()
{
// 生产者数量
std::queue<int> 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<std::mutex> 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<std::mutex> 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 <vector> // std::vector
#include <queue> // std::queue
#include <memory> // std::make_shared
#include <stdexcept> // std::runtime_error
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <future> // std::future, std::packaged_task
#include <functional> // std::function, std::bind
#include <utility> // std::move, std::forward
class ThreadPool {
public:
inline ThreadPool(size_t threads) : stop(false) { //构造函数,且把stop变量赋值为false
for(size_t i = 0;i<threads;++i)//创造线程实例
workers.emplace_back([this] {//使用lambda表达式返回this
for(;;)
{
std::function<void()> task;//function函数对象类,可调用实体的一种类型安全的包裹
{
std::unique_lock<std::mutex> lock(this->queue_mutex);//互斥量上锁
//std::cout<<"thread"<<std::this_thread::get_id()<<"begin work"<<std::endl;
this->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"<<std::this_thread::get_id()<<"begin work"<<std::endl;
});
}
inline ~ThreadPool() {
{
//std::unique_lock<std::mutex> lock(queue_mutex);//互斥量上锁,避免
stop = true;
}
condition.notify_all();//通知所有的休眠线程
for(std::thread &worker: workers)
worker.join();
}
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)//可变参数的模板,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::future 对象以供实施线程同步
{
std::unique_lock<std::mutex> 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<std::thread> workers;//线程池
std::queue<std::function<void()>> 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; //消费数据
}
}