c++11——多线程
c++11中增加了线程以及线程相关的类,很方便的支持了并发编程。
1. 线程
线程创建
使用std::thread创建线程,提供线程函数或者函数对象即可,并且可以指定线程函数的参数。
#include<thread>
void func(int a){
cout << "param= " << a << endl;
}
int main(){
std::thread t(func, 10); //创建线程使用线程函数以及函数参数
t.join(); //等待线程函数执行完毕
return 0;
}
可以通过std::bind或lambda表达式来创建线程:
void func(int a,int b){
cout << "a = " << a << ", b = " << b << endl;
}
int main(){
std::thread t1(std::bind(func, 1, 2));
std::thread t2([](int a, int b){cout << "a = " << a << ", b = " << b << endl;}, 1,2);
return 0;
}
join 和 detach
如果希望在主线程中等待其他线程终止,使用join来实现,t.join()阻塞等待线程t结束,才能继续往下执行;
如果不希望线程被阻塞执行,可以调用 t.detach() 方法,将线程和线程对象分离,分离之后,线程单独执行,由系统回收资源。通过detach,线程和线程对象分离,让线程作为后台线程执行,当前线程也不会被阻塞。但需要注意,detach之后,就无法再和线程发生联系,不能通过join再阻塞等待线程,线程何时执行完我们也无法控制。
若在创建子线程的线程中,既没有执行对子线程进行join也没有进行detach,则有可能出现线程对象在线程函数结束之前就失效,从而出错。
int main(){
std::thread t(func);
t.detach();
//做其他事情....
return 0;
}
线程生命期
std::thread在出了作用域之后就会被析构,这是如果线程函数还没有被执行完,就会发生错误。因此,需要保证线程函数的生命周期在线程变量 std::thread的生命周期之内,这可以通过join阻塞等待实现。
线程不能被复制,但可以移动,比如:
#include<thread>
void func(){
//do some work
}
int main(){
std::thread t(func);
std::thread t1(std::move(t));
t.join();
t1.join();
return 0;
}//线程被移动(std::move)后,线程对象t就不代表任何线程了。
线程基本用法
1. 获取当前信息
void func(){
}
int main(){
std::thread t(func);
cout << t.get_id() << endl; //获取线程id
cout << std::thread::hardware_concurrency() << endl; //获取cpu核数
return 0;
}
2. 线程休眠
void f(){
std::this_thread::sleep_for(std::chrono::seconds(3)); //当前线程休眠3秒钟
}
2. 互斥量
互斥量是一种同步原语,是一种线程同步的手段,用来保护多线程同时访问的共享数据。
c++11中提供了四种语义的互斥量(mutex):
std::mutex 独占的互斥量,不能递归使用
std::timed_mutex 带超时的独占互斥量,不能递归使用
std::recursive_mutex 递归互斥量,不带超时功能
std::recursive_timed_mutex 带超时的递归互斥量
std::mutex
std::mutex g_lock;
void func(){
g_lock.lock();
std::cout << "entered thread " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "leave thread " << std::this_thread::get_id() << endl;
g_lock.unlock();
}
std::recursive_mutex
递归锁允许同一线程多次获得该互斥锁,可以用来解决同一线程需要多次获取互斥量时死锁的问题。但尽量不要使用递归锁,因为:
(1)需要用到递归锁定的多线程往往本身是可以简化的
(2)递归锁比起非递归锁,效率会低一些
(3)递归锁虽然允许同一线程多次获得同一个互斥量,可重复获得的最大次数并未具体说明,一旦超过次数,再对lock进行调用就会抛出std::system错误。
std::timed_mutex
std::timed_mutex是超时的独占锁,主要用于在获取锁时增加超时等待功能,在超时时间之内如果获得锁,则继续,否则,返回超时,并不再阻塞。
std::time_mutex 使用 try_for_lock(timexx)来尝试获取锁,在超过timexx时间仍未获得锁,则返回超时。
std::time_mutex mut;
void work(){
std::chrono::milliseconds timeout(100);
while(true){
if(mut.try_lock_for(time_out)){
std::cout << std::this_thread::get_id() << " :do work with the mutex" << endl;
std::chrono::milliseconds sleepDuration(250);
std::this_thread::sleep_for(sleepDuration);
mut.unlock();
}else{
std::cout << "time out for get lock" << endl;
}
}
}
lock_guard
lock_guard利用RAII机制,防止忘记unlock或者发生异常时无法unlock造成程序死锁。
std::mutex mut;
void func(){
std::lock_guard<std::mutex> guard(mut);
xxxx
}
3. 条件变量
条件变量能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时,才会唤醒当前阻塞的线程。条件变量需要和互斥量配合起来使用一。c++11提供了两种条件变量: condition_variable, 配合std::unique_lock<std::mutex>进行wait操作
condition_variable_any,和任意带有lock,unlock语义的mutex搭配使用,比较灵活,
但效率比condition_varaible略低
void Put(const T& x){
std::lock_guard<std::mutex> locker(m_mutex);
while(IsFull()){
cout << "缓冲区满,需要等待..." << endl;
m_notFull.wait(m_mutex);
}
m_queue.push_back(x);
m_notEmpty.notify_one();
} void Take(T& x){
std::lock_guard<std::mutex> locker(m_mutex);
while(IsEmpty()){
cout << "缓冲区空,需要等待...." << endl;
m_notEmpty.wait(m_mutex);
}
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
std::list<T> m_queue;
std::mutex m_mutex;
std::condition_variable_any m_notEmpty;
std::condition_variable_any m_notFull;
4. 原子变量
c++11提供了一个原子类型 std::atomic< T>,可以使用任意类型作为模板参数,c++11内置了整型的原子变量,可以更方便的使用原子变量,使用原子变量就不需要使用互斥量来保护该变量了。
#include<atomic>
struct AtomicCounter{
std::atomic<int> value;
void inc(){
++value;
}
void dec(){
--value;
}
int get(){
return value.load();
}
};
5. call_once/once_flag使用
为了保证在多线程环境中某个函数仅被调用一次,或者某个变量仅仅被初始化一次,可以使用 std::call_once来保证函数在多线程环境中只被调用一次。使用std::call_once时,需要一个once_flag作为call_once的入参。
std::once_flag flag;
void do_once(){
std::call_once(flag, [](){ std::cout << "called once" << endl;});
}
6. 异步操作
c++11中提供了异步操作相关的类,主要有 std::future, std::promise, std::package_task. std::future 作为异步结果的传输通道,很方便的获取线程函数的返回值; std::promise用来包装一个值,将数据和future绑定起来,方便线程赋值;std::package_task用来包装一个可调用对象,将函数和future帮顶起来,以便异步调用。
std::future 获取线程函数返回值
c++ thread库提供了future用来访问异步操作的结果,future提供了获取异步操作结果的通道,可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。
future_status == Deferred 异步操作还没开始
future_status == Ready 异步操作已经完成
future_status == Timeout 异步操作超时
std::future_status st;
do{
st = future.wait_for(std::chrono::seconds(1));
if(st == std::future_status::deferred){
//还没开始
}else if(st == std::future_status::timeout){
//超时
}else if(st == std::future_status::ready){
//ready
}
}while(st != std::future_status::Ready);
获得future结果的三种方式: get, wait, wait_for, 其中get等待异步操作的结果并返回结果;wait只是等待异步操作完成,没有返回值;wait_for是超时等待返回的结果。
std::promise 协助线程赋值的类
std::promise 将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promise的future获取该值。
std::promise<int> pr;
std::thread t([](std::promise<int>& p)
{ p.set_value_at_thread_exit(9);}, //在线程函数中为promise赋值
std::ref(pr)); //线程函数的参数,使用引用 std::future<int> f = pr.get_future(); //获取promise的future
auto r = f.get(); //利用get等待异步操作的结果(阻塞)
std::packaged_task 可调用对象的包装类
std::packaged_task可以将函数等可调用对象和future绑定起来,以便异步调用。
std::packaged_task<int()> task([]{return 7;});
std::thread t1(std::ref(task));
std::future<int> f1 = task.get_future();
auto r1 = f1.get();
std::promise, std::packaged_task, std::future三者之间的关系
std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次的对象。std::future之上的高一层为 std::packaged_task和std::promise, 他们内部都有future以便访问异步操作的结果,std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值,都是为了方便异步操作。
future被promise和packaged_task用来作为异步操作或异步结果的连接通道,用std::future和std::shared_future来获取异步调用的结果。future不可拷贝,只能移动,shared_future可以拷贝,当需要将future放到容器中则需要用shared_future。
#include<iostream>
#include<utility>
#include<future>
#include<vector>
#include<thread> int func(int x){
return x*x;
} int main(){
std::packaged_task<int(int)> tsk(func);
std::future<int> fut = tsk.get_future(); std::thread(std::move(tsk), 2).detach(); int value = fut.get();
std::cout << "The result is " << value << ".\n"; //std::future不可复制,无法放到容器中,需要用shared_future
std::vector<std::shared_future<int>> v;
std::shared_future<int> f = std::async(std::launch::async, [](int a, int b){ return a + b; }, 2, 3);
v.push_back(f); std::cout << "The shared future result is " << v[0].get() << "\n";
return 0;
}
std::async 线程异步操作函数
std::async比std::promise, std::packaged_task, std::thread更高一层,可以用来直接创建异步的task, 异步任务返回的结果也保存在future中,当需要获取异步任务的结果时,只需要调用future.get() 方法即可,如果不关注异步任务的结果,只是简单的等待任务完成的话,则调用 future.wait()方法。
std::async(std::launch::async | std::launch::deferred, f, args....)
,第一个参数是线程创建的策略,默认策略为 std::launch::async。
std::launch::async 在调用 async时就开始创建线程
std::launch::deferred 延迟加载方式创建线程。调用 async时不创建线程,直到调用了future的get或者wait方法时才创建线程。
#include<iostream>
#include<thread>
#include<future>
#include<utility>
int main(){
std::future<int> f1 = std::async(std::launch::async, [](){
return 8;
}); std::cout << f1.get() << std::endl; std::future<void> f2 = std::async(std::launch::async, [](){
std::cout << 8 << std::endl;
}); f2.wait(); std::future<int> future = std::async(std::launch::async, [](){
std::this_thread::sleep_for(std::chrono::seconds(3));
return 8;
}); std::cout << "waiting ... " << std::endl;
std::future_status status; do{
status = future.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred){
std::cout << "deferred..\n";
}else if(status == std::future_status::timeout){
std::cout << "time out...\n";
}
else if (status == std::future_status::ready){
std::cout << "ready!\n";
}
} while (status != std::future_status::ready);
std::cout << "result is " << future.get() << std::endl;
return 0;
}
执行结果为:
c++11——多线程的更多相关文章
- C++11多线程教学(二)
C++11多线程教学II 从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程 ...
- C++11多线程教学(一)
本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...
- C++11多线程教学II
从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程序,但是我们回避了线程同步 ...
- c++ 11 多线程教学(1)
本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...
- 【转】C++ 11 并发指南一(C++ 11 多线程初探)
引言 C++ 11自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些C++ 11的新特性,算是记录一下自己学到的东西吧,和大家共勉. 相信Linux程序员都用过Pthrea ...
- C++11 并发指南一(C++11 多线程初探)
引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...
- C++11 多线程编程 使用lambda创建std::thread (生产/消费者模式)
要写个tcp server / client的博客,想着先写个c++11多线程程序.方便后面写博客使用. 目前c++11中写多线程已经很方便了,不用再像之前的pthread_create,c++11中 ...
- C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题——生产者消费者模型,并给出 ...
- C++11多线程教学
转自:http://www.cnblogs.com/lidabo/p/3908705.html 本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads ...
- Linux下c++11多线程聊天室
刚看的c++11多线程,写个聊天室试试编译的时候加上 c++11 和 多线程库g++ -Wall -std=c++0x -pthread -o server server.cppserver 和cli ...
随机推荐
- IOS性能调优系列:使用Zombies动态分析内存中的僵尸对象
硬广:<IOS性能调优系列>第四篇,预计会有二十多篇,持续更新,欢迎关注. 前两篇<IOS性能调优系列:Analyze静态分析>.<IOS性能调优系列:使用Instrum ...
- Openresty支持HTTP2
1. 下载openresty-1.13.6.1.tar.gz和openssl-1.0.2l.tar.gz,并解压 下载对应的软件版本,创建openresty_http2安装路径 2. 安装openre ...
- atitit.软件开发概念--过滤和投影 数据操作
atitit.软件开发概念--过滤和投影 数据操作 投影的本质及扩展 物体在太阳光的照射下形成的影子(简称日影)就是平行投影.日影的方向可以反映时间 投影还比喻此物通过彼物表现出来的迹象. 作者::老 ...
- centos 搭建nginx
yum install wget yum install gcc-c++ yum -y install pcre prec-devel yum -y install zlib zlib-devel y ...
- 今天遇到个PHP不知原因的报内部错误
今天遇到个PHP不知原因的报内部错误 纠结了很久想尽了办法,1.apache日志 2.错误级别 ,还差点就把自己写的那个破烂不堪的日志系统加上去了 纠结了很久还是无果,在最终,最终发现了 原来是类命名 ...
- ldap 使用 问题参考
Q2.ldapsearch查询一个有30000多条记录时出现:Size limit exceeded 4 A2:服务器端配置文件有sizelimit 1000的限制!用管理员身份查询-D"c ...
- 【pywin32总结】
#下面是必备的#注意!所有方法后面都要加括号()!!! import win32com from win32com.client import Dispatch,constants w = win32 ...
- 基于HTML5坦克大战游戏简化版
之前我们有分享过不少经典的HTML5游戏,有些还是很有意思的,比如HTML5版切水果游戏和HTML5中国象棋游戏.今天要分享的是一款简化版的HTML5坦克大战游戏,方向键控制坦克的行进方向,空格键发射 ...
- OpenGL ES 简单教程
什么是OpenGL ES? OpenGL ES (为OpenGL for Embedded System的缩写) 为适用于嵌入式系统的一个免费二维和三维图形库. 为桌面版本号OpenGL 的一个子集. ...
- go web框架推荐
https://blog.usejournal.com/top-6-web-frameworks-for-go-as-of-2017-23270e059c4b https://www.zhihu.co ...