C++11多线程与线程管理

C++11多线程与线程管理 一、线程基础1.1 thread默认构造函数std::thread::thread()_NOEXCEPT{_Thr_set_null(_Thr);}默认构造函数创建一个空线程对象不关联任何执行线程。1.2 thread带参数构造函数explicitthread(Fn,Args...);可变参数模板可以传入不同的参数组合std::threadt1(func,arg1,arg2);std::threadt2(Class::memberFunc,instance,arg);1.3 拷贝构造函数禁用thread(constthread)delete;// 拷贝构造函数被禁用不允许thread t2 t1;线程对象不可拷贝只能移动。二、线程操作详解2.1 基本操作函数函数说明get_id()获取线程IDjoinable()判断线程是否可joinjoin()等待线程结束并回收资源detach()将线程与主线程分离2.2 join与detach的区别join()特点调用join()后线程不会再被调用主线程会阻塞等待该线程结束join()完成后线程资源被回收std::threadt(func);t.join();// 主线程等待t结束detach()特点分离后的线程称为后台线程主线程退出时其他线程可能未运行完就被迫退出detach后无法再管理该线程detach后不能调用joinstd::threadt(func);t.detach();// 分离线程// 之后无法再管理t2.3 joinable()与move操作std::threadt1(func);std::thread t2std::move(t1);// t1的资源转移到t2// 此时 t1.joinable() false// 对 t1 调用 join 会抛出异常注意对线程调用move后原线程对象变为空壳不能再join。三、线程封装与继承3.1 子类继承模式通过封装thread实现业务逻辑与线程管理的分离classZERO_Thread{public:voidstart(){thread_std::thread(ZERO_Thread::threadEntry,this);}protected:virtualvoidrun()0;// 子类实现具体业务private:voidthreadEntry(){running_true;try{run();// 调用子类的run函数}catch(std::exceptionex){running_false;throwex;}catch(...){running_false;throw;}running_false;}std::thread thread_;boolrunning_false;};流程图start() | v thread_ thread(threadEntry) | v threadEntry() [新线程] | v running_ true | v try { run(); } | v catch (...) [如有异常] | v running_ false四、参数传递注意事项4.1 传入引用需要ref()voidfunc(intx){x10;}inta0;std::threadt(func,std::ref(a));// 必须用ref包装t.join();4.2 传入类成员函数classA{public:voidsetName(conststringname){name_name;}voidfunc4(inta){/* ... */}};A*a4_ptrnewA();a4_ptr-setName(darren);std::threadt4(A::func4,a4_ptr,10);t4.join();deletea4_ptr;4.3 类成员函数重载的处理// 指向void func4(int)的指针std::threadt41((void(A::*)(int))A::func4,a4_ptr,100);// 指向int func4(string)的指针std::threadt43((int(A::*)(string))A::func4,a4_ptr,hello);bind也支持重载std::bind((void(A::*)(int))A::func4,a4_ptr,100);五、互斥量与锁5.1 mutex系列类型说明mutex基本互斥量timed_mutex带超时功能的互斥量recursive_mutex递归互斥量可重入recursive_timed_mutex递归超时基本用法std::mutex mtx;mtx.lock();// 临界区代码mtx.unlock();if(mtx.try_lock()){// 尝试获取锁// ...mtx.unlock();}5.2 lock_guard与unique_locklock_guard特点构造时自动加锁析构时自动解锁不能手动解锁std::lock_guardstd::mutexlck(mtx);// 进入临界区// 离开作用域自动解锁unique_lock特点更灵活可手动解锁支持延迟加锁支持条件变量std::unique_lockstd::mutexlck(mtx);lck.unlock();// 手动解锁// ...lck.lock();// 再次加锁// 支持 scoped_lock 等高级用法对比表特性lock_guardunique_lock自动加锁是是自动解锁是是手动解锁否是条件变量否是开销较小较大六、条件变量6.1 wait机制死等waitstd::mutex mtx;std::condition_variable cv;std::unique_lockstd::mutexlck(mtx);cv.wait(lck);// 一直等待直到被唤醒超时等待wait_forautostatuscv.wait_for(lck,std::chrono::seconds(5));if(statusstd::cv_status::timeout){// 超时退出}else{// 被唤醒}6.2 唤醒操作函数说明notify_one唤醒一个等待的线程notify_all唤醒所有等待的线程示例// 生产者{std::lock_guardstd::mutexlck(mtx);queue.push(data);}cv.notify_one();// 唤醒一个消费者// 消费者std::unique_lockstd::mutexlck(mtx);cv.wait(lck,[]{return!queue.empty();});// 虚假唤醒保护autodataqueue.front();queue.pop();七、原子变量atomic7.1 基本用法#includeatomicstd::atomicintcount(0);// 初始化为0count;// 原子操作count.fetch_add(1);// 显示原子加count.store(10);// 原子写intvalcount.load();// 原子读特点无锁操作保证原子性适用于计数器、标志位等简单变量比mutex效率更高八、异步与future8.1 std::async异步运行std::futureintfstd::async(std::launch::async,func,arg);intresultf.get();// 阻塞等待结果8.2 std::packaged_task将任务与future绑定std::packaged_taskint(int,int)task(add);std::futureintftask.get_future();std::threadt(std::move(task),1,2);t.join();intresultf.get();8.3 std::promise设置值std::promiseintp;std::futureintfp.get_future();p.set_value(42);// 设置值intresultf.get();// 获取三者关系组件作用std::async异步启动任务std::packaged_task绑定任务和futurestd::promise主动设置值九、function、bind与lambda9.1 std::function保存函数#includefunctionalstd::functionint(int,int)func1_;func1_add;// 保存普通函数func1_[](inta,intb){returnab;};// 保存lambda9.2 std::bind绑定函数autofstd::bind(add,1,std::placeholders::_1);intresultf(2);// 相当于add(1, 2)注意占位符从_1开始不是_0可以预先绑定部分参数9.3 lambda表达式autof[](inta,intb)-int{returnab;};intresultf(1,2);十、可变模板参数templateclass...Tvoidf(T...args){// sizeof...(args) 获取参数个数}十一、C11线程池实现11.1 核心结构------------------ | ThreadPool | ------------------ | threads_: 4个 | | taskQueue_ | | mutex_ | | condition_ | ------------------ | -------------------------------------- | | | v v v -------- -------- -------- |thread1 | |thread2 | |thread3 | -------- -------- -------- | | | v v v 从taskQueue_取任务执行11.2 简单实现classThreadPool{public:ThreadPool(intnumThreads){for(inti0;inumThreads;i){threads_.emplace_back([this]{while(true){std::functionvoid()task;{std::unique_lockstd::mutexlck(mtx_);condition_.wait(lck,[this]{return!tasks_.empty();});taskstd::move(tasks_.front());tasks_.pop();}task();}});}}templateclassF,class...Argsautoexec(Ff,Args...args)-std::futuretypenamestd::result_ofF(Args...)::type{usingreturn_typetypenamestd::result_ofF(Args...)::type;autotaskstd::make_sharedstd::packaged_taskreturn_type()(std::bind(std::forwardF(f),std::forwardArgs(args)...));std::futurereturn_typerestask-get_future();{std::lock_guardstd::mutexlck(mtx_);tasks_.push([task](){(*task)();});}condition_.notify_one();returnres;}~ThreadPool(){for(autot:threads_){t.join();}}private:std::vectorstd::threadthreads_;std::queuestd::functionvoid()tasks_;std::mutex mtx_;std::condition_variable condition_;};11.3 使用示例ThreadPoolpool(4);// 执行普通函数autof1pool.exec(add,1,2);// 执行类成员函数A obj;autof2pool.exec(A::func,obj,10);intresultf1.get();// get是阻塞的十二、线程睡眠与ID12.1 睡眠函数函数说明std::this_thread::sleep_for(duration)睡眠指定时长std::this_thread::sleep_until(time_point)睡眠到指定时间std::this_thread::yield()让出CPU时间片12.2 获取线程IDstd::this_thread::get_id();// 获取当前线程IDthread.get_id();// 获取thread对象的线程ID总结C11多线程核心要点类别要点thread不可拷贝可移动join/detach二选一mutexlock/unlock/try_lockRAII用lock_guard/unique_lock条件变量wait/wait_for notify_one/notify_allatomic无锁原子操作适用于简单类型async/future异步任务与结果获取packaged_task任务与future绑定promise主动设置异步结果function/bind函数包装与绑定lambda匿名函数对象线程池生产者-消费者模式任务队列根据零声教育教学写作https://github.com/0voice