C++标准线程库(std::thread)
五个包含头文件:
<atomic> :引入std::atomic和std::atomic_flag,定义了原子操作
<thread> :声明了std::thread和std::this_thread命名空间
<mutex> :声明了与互斥量mutex相关的类
<condition_variable> :声明了与条件变量相关的类
<future>
创建线程:
std::thread::id main_thread_id = std::this_thread::get_id();
void hello()
{
cout << "hello world ";
if (main_thread_id == std::this_thread::get_id())
{
cout << "这是主线程" << endl;
}
else
{
cout << "这不是主线程" << endl;
}
}
void pause_thread(int n)
{
std::this_thread::sleep_for(std::chrono::seconds(n));
cout << "花费 " << n << " 时间 " << endl;
}
int main()
{
thread T(hello);
cout << T.hardware_concurrency() << endl; //用于获取程序可以调动的最大线程数,仅作参考
cout << "native_handle" << T.native_handle() << endl; //只有库函数支持该函数时该方法才会有效。
//如果有效,用于获得与操作系统相关的原生线程句柄
T.join();
thread a(hello);
a.detach();
thread threads[5];
cout << "Done threadsad" << endl;
for (int i = 0; i < 5; ++i)
{
threads[i] = std::thread(pause_thread, i + 1);
}
for (auto& t : threads)
{
t.join();
}
cout << "All threads joind" << endl;
return 0;
}
join()函数:汇合线程,阻塞主线程,等待子进程执行结束,才会回到主线程
detach()函数:打破依赖关系,把子线程驻留后台.
joinable()函数:判断当前线程是否可以做join操作或者detach操作
可被 joinable
的 std::thread
对象必须在他们销毁之前被主线程 join
或者将其设置为 detached
.
hardware_concurrency():用于获取程序可以调动的最大线程数,仅作参考
native_handle():只有库函数支持该函数时该方法才会有效。如果有效,用于获得与操作系统相关的原生线程句柄。
get_id
:获取线程ID,返回一个类型为std::thread::id的对象。
Swap
: 交换两个线程对象所代表的底层句柄(underlying handles
)。
yield: 当前线程放弃执行,操作系统调度另一线程继续执行。
sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。
sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 sleep_duration 所表示的时间片更长。
WaitForSingleObject():
等待指定线程结束
语法:WaitForSingleObject(hThread, INFINITE);
第一参数是创建线程的时候可得到的返回值。第二个参数不用关心,传入INFINITE就可以了
有了这个函数,再也不用Sleep或者While(1)啦。
CreateMutex():
多线程资源加锁
使用环境:
1、创建多个线程,使“输出”这个资源,只能在一个时间内被单个线程使用
2、为资源加锁:
SetThreadAffinityMask():
指定线程运行核心。
DWORD_PTR SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
第一个参数是线程的句柄(HANDLE)
第二个参数用来指定CPU核心
比如,你要指定进程到第0个CPU上,则mask=0×01
第1个CPU:mask=0×02
第2个CPU:mask=0×04 (注意不是0×03)
第3个CPU:mask=0×08
以此类推。
如果要指定多个CPU:
比如第0、1个:mask=0×03
第1、2个:mask=0×06
以此类推。
如果CPU个数不足,则会进行取模操作。比如一共4个CPU,则mask=0×0010则和0×01一样
6.28强化版:
第一节:
并发的概念:
两个或者更多的任务(独立的活动)同时发生(进行),一个程序同时执行多个独立的任务
以往计算机,单核cpu某一个时刻只能执行一个任务,由操作系统调度,每秒钟进行多次所谓的”任务切换“。这是并发的假象,不是真正的并发。这种切换是要有时间开销的
使用并发的原因:提高性能
可执行程序概念:
windows下,一个扩展名为.exe的。
进程的概念:
就是一个可执行程序运行起来就叫创建了一个进程
线程的概念:
每个进程(执行起来的可执行程序),都有一个主线程,这个主线程是唯一的,也就是一个进程中只能有一个主线程。一个进程中最少也是有一个线程(主线程)。
只要进程执行了,那么主线程就随着进程默默启动
多线程(并发):
线程并不是越多越好,每个线程,都需要一个独立的堆栈空间,线程之间的切换要保存很多中间的状态。
切换就会耗费本该属于程序运行的时间!
并发的实现方式:
两个或者多个任务(独立的活动)同时发生(进行)。
手段:
1、我们通过多个进程实现并发。
2、在单独的进程中创建多个线程实现并发。
多进程开发:
进程之间通信:同一个电脑上:管道、文件、消息对列、共享内存
不同电脑上:socket通信技术
多线程并发:
单个进程中,创建了多个线程。
一个进程中所有的线程共享地址空间(共享内存)。
全局变量、指针、引用都可以在线程之间传递
所以使用多线程开销小于多进程。
但是内存共享带来的问题:数据一致性问题!
以往多线程代码不能跨平台。
用POSIX thread(pthread)可以跨平台但需要一些配置。
第二节:
线程基础:
void test01()
{
cout << "线程开始执行" << endl;
cout << "线程执行完毕" << endl;
}
//一、演示线程运行时的开始和结束
//主线程的执行,主线程从main()函数返回
//则整个进程执行完毕
//cout << "hello world" << endl;
//主线程从main()函数开始执行,那么我们创建的线程也需要从一个函数开始
//一旦函数运行完毕,代表我们这个线程运行结束
//整个进程是否执行完毕的标志是主线程是否执行完毕
//一般情况下如果子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止
//创建线程包含头文件#include<thread>
//thread是标准库类
thread mytobj(test01); //test01可调用对象
//mytobj.join(); //阻塞主线程,让主线程等子线程执行完毕。然后子线程跟主线程回合,主线程再继续往下走
mytobj.detach(); //分离 主线程不和子线程汇合 此时这个子线程就会驻留在后台运行,当子线程执行完毕后由运行时库负责清理该线程相关的资源
//detach使线程失去我们自己的控制
mytobj.joinable(); //判断是否可以成功使用join或者detach
其他方式创建线程:
//类创建线程对象
class Ta
{
public:
void operator()() //不能调用参数
{
cout << "我的线程开始执行" << endl;
cout << "我的线程结束" << endl;
}
};
int main()
{
//Ta ta;
//thread mytob(ta); //ta可调用对象
//mytob.join(); //
//mytob.detach(); //一旦调用了detach 那主线程结束了这个ta对象还在吗?不在了!但是这个对象实际上是被复制到线程中去,执行完主线程后,ta会被销毁,但是所复制的ta对象依然存在。
//所以只要你这个ta类对象里没有引用,没有指针,那么就不会产生问题。
//lambda表达式 创建自己的线程对象
auto mythread = [] {
cout << "我的线程开始执行" << endl;
cout << "我的线程结束" << endl;
};
thread myobj(mythread);
myobj.join();
return 0;
}
第三节:
#include<iostream>
#include<vector>
#include<string>
#include<thread>
using namespace std;
class A
{
public :
int m_u;
//类型构造函数 可以把int整形转换成一个类A对象
A(int a) :m_u(a)
{
cout << "A a 构造函数执行" <<&m_u<< endl;
}
A(const A& a) : m_u(a.m_u)
{
cout << "A a 拷贝构造函数执行" << &m_u << endl;
}
~A()
{
cout << "A a析构函数执行" << &m_u << endl;
}
};
//void myprint(const int& i, const string& p)
//{
// cout << i << endl;//经过测试分析认为此处i并不是myi的引用,实际是值传递,那么即使主线程detach了子线程那么子线程中用myi的值仍然是安全的。
// cout << p << endl;//指针在detach子线程时,绝对会有问题 所以创建线程不建议用引用和指针!
// return;
//}
void myprint(const int& i, const A & p)
{
cout << &p << endl; //打印p对象的地址
}
int main()
{
一、传递临时对象作为线程参数
陷阱1:
//int myi = 1;
//int& y = myi;
//char my[] = "this is texs";
//thread myobj (myprint, myi, my);
//cout << "in my world " << endl;
//myobj.join();
myobj.detach();
//二
//int i = 1;
//int sec = 12;
//thread myobj(myprint, i, sec); //将sec转化为A类型对象!传递给myprint第二个参数
//myobj.join();
//myobj.deatch(); //用deacth 会发发现构造发生在main函数执行后!
//所以此处整形转换也有可能导致失效!
int i = 1;
int sec = 12;
thread myobj(myprint, i, A(sec));
//由此可见创建线程的同时构造临时对象传递参数的方法是可行的
myobj.detach();
return 0;
}
#if 0
将char[]转化为string
但这种情况存在一个问题,就是主程序已经执行完了,my[]都被回收了 再去转化为string 的可能性
优化写法:
thread mytobj(myprint, y, string(my)); == 》 void myprint(const int& i,const string &p) 临时构造一个string对象
这是一个可以保证线程中使用的对象肯定稳定有效的方法
总结:
1:只要临时构造类A对象作为参数传递给线程,那么就一定能在主线程执行完毕之前把线程函数的第二个参数构建出来,即便是deatch(),也能保证子线程安全执行!
2:若传递int这种简单类型参数,建议都是值传递,不要用引用
3:如果传递类对象,避免隐式类型转换。构建临时对象然后在函数参数里用引用接,否则系统会多构造一次对象
4:建议不适用deatch(),只是用join(),这样就不存在局部变量失效导致线程对内存的非法引用问题。
#endif // 0
第四节:
创建和等待多个线程:
#include<iostream>
#include<thread>
#include<vector>
using namespace std;
#if 0
多个线程执行是乱的,跟操作系统内部对线程的运行调度机制有关
主线程等待所有子线程运行结束,然后主线程结束。用join更容易写出稳定的程序
把thread对象放入到容器里管理,这对一次性创建大量线程跟管理这些线程比较方便
#endif // 0
void printY(int num)
{
cout << "子线程开始执行了,线程编号 = " << num << endl;
cout << "子线程结束执行了,线程编号 = " << num << endl;
}
int main()
{
vector<thread>m1;
for (int i = 0; i < 10; ++i)
{
m1.push_back(thread(printY, i));
}
for (auto i = m1.begin(); i != m1.end(); i++)
{
i->join();
}
cout << "主线程结束" << endl;
return 0;
}
数据共享问题:
只读数据:
#include<iostream>
#include<thread>
#include<vector>
using namespace std;
vector<int> v1 = { 1,2,3 }; //共享数据 只读
#if 0
只读数据 安全稳定 不需要特别的处理手段 直接读取就可以
#endif // 0
void printY(int num)
{
cout << "id = " << std::this_thread::get_id()<<"的线程 打印vector的值"<<v1[0]<<v1[1]<<v1[2]<<endl;
return;
}
int main()
{
vector<thread>m1;
for (int i = 0; i < 10; ++i)
{
m1.push_back(thread(printY, i));
}
for (auto i = m1.begin(); i != m1.end(); i++)
{
i->join();
}
cout << "主线程结束" << endl;
return 0;
}
有读有写数据:
两个线程写,八个线程读 若是没有特别处理肯定是崩溃的
所以最简单的不崩溃处理,读的时候不写,写的时候不读 不能同时写,不能同时读
像这么写的时候就会崩溃
#include<iostream>
#include<thread>
#include<vector>
#include<list>
using namespace std;
#if 0
#endif // 0
class A
{
public:
//接受消息
void setX()
{
for (int i = 0; i < 1000; ++i)
{
cout << "setX执行 插入一个元素: " << i << endl;
l1.push_back(i); //假设i为收到的信息
}
}
//取出信息
void printX()
{
for (int i = 0; i < 1000; ++i)
{
if (!l1.empty())
{
int x = l1.front();
l1.pop_front();
}
else
{
cout << "没有信息" <<i<< endl;
}
}
cout << "消息处理结束" << endl;
}
private:
list<int> l1; //存放信息
};
int main()
{
A a;
thread mySet(&A::setX, &a);
thread myPrint(&A::printX, &a);
mySet.join();
myPrint.join();
return 0;
}
解决方式:
首先了解互斥量的概念:
互斥量是一个类对象,可以理解成一把锁,多个线程尝试用lock()成员函数来加锁但只有一个线程能够锁定成功,如果没有锁定成功,那么线程会卡在lock()这里不断尝试加锁。
用法:
使用前包含头文件#include<mutex>
先lock()加锁,操作共享数据,再用unlock()解锁,成对使用。
#include<iostream>
#include<thread>
#include<vector>
#include<list>
#include<mutex>
using namespace std;
#if 0
#endif // 0
class A
{
public:
//接受消息
void setX()
{
for (int i = 0; i < 10000; ++i)
{
cout << "setX执行 插入一个元素: " << i << endl;
mymutex.lock();
l1.push_back(i); //假设i为收到的信息
mymutex.unlock();
}
}
//取出信息
void printX()
{
for (int i = 0; i < 10000; ++i)
{
if (!l1.empty())
{
mymutex.lock();
int x = l1.front();
l1.pop_front();
mymutex.unlock();
}
else
{
cout << "没有信息" <<i<< endl;
}
}
cout << "消息处理结束" << endl;
}
private:
list<int> l1; //存放信息
mutex mymutex; //创建一个互斥量
};
int main()
{
A a;
thread mySet(&A::setX, &a);
thread myPrint(&A::printX, &a);
mySet.join();
myPrint.join();
return 0;
}
lock_guard类模板:使用后不能再使用lock和unlock
#include<iostream>
#include<thread>
#include<vector>
#include<list>
#include<mutex>
using namespace std;
#if 0
lock_guard<mutex> lg1(mymutex); 声明格式
lock_guard的构造函数执行了mutex::lock()
析构执行了unlock();
#endif // 0
class A
{
public:
//接受消息
void setX()
{
for (int i = 0; i < 10000; ++i)
{
cout << "setX执行 插入一个元素: " << i << endl;
lock_guard<mutex> lg1(mymutex);
l1.push_back(i); //假设i为收到的信息
}
}
//取出信息
void printX()
{
for (int i = 0; i < 10000; ++i)
{
if (!l1.empty())
{
lock_guard<mutex> lg2(mymutex);
int x = l1.front();
l1.pop_front();
}
else
{
cout << "没有信息" <<i<< endl;
}
}
cout << "消息处理结束" << endl;
}
private:
list<int> l1; //存放信息
mutex mymutex; //创建一个互斥量
};
int main()
{
A a;
thread mySet(&A::setX, &a);
thread myPrint(&A::printX, &a);
mySet.join();
myPrint.join();
return 0;
}
若是限定lock_guard的生命周期,可以加{}限定
死锁:
死锁产生的条件是至少有两个互斥量
只要保持两个互斥量的顺序一致就不会出现死锁
std::lock()函数模板:
一次锁住两个或者两个以上的互斥量(一个不行) 不存在因为在多个线程中因为锁的顺序而产生死锁 处理多个互斥量才使用
std::lock_guard的std::adopt_lock参数:
lock(my_mutex1,my_mutex2);
语法:lock_guard<std::mutex>lg1(mymutex1,std::adopt_lock);
std::adopt_lock结构体对象,表示这个互斥量已经lock 无需再在lock_guard<std::mutex>lg1构造函数里再次调用lock()
第六节:
unique_lock取代lock_guard:
unique_lock比lock_guard灵活很多,但是内存占用多一些,效率差一些
unique_lock的第二个参数:
- std::adopt_lock:表示这个互斥量已经被lock了(互斥量必须提前被lock)否则会报异常
- 补充:延迟线程:std::chrono::milliseconds i(20000); std::this_thread::sleep_for(i);
- std::try_to_lock:尝试去给线程加锁 前提是线程并没有进行lock() 可以用owns_clock()判断是否拿到锁,若是没拿到锁可以进行其他操作
unique_lock<std::mutex>l11(mymutex, std::try_to_lock); if (l11.owns_lock()) { int x = l1.front(); l1.pop_front(); } else { cout << "拿不到锁,只能干点别的事情咯" << endl; }
4.std::defer_lock():使用前提是不能先lock() 作用是可以初始化一个没有加锁的mutedef
unique_lock的成员函数:
lock() unlock() try_lock() release()
release() : 返回它所管理的mutex对象指针,并释放所有权
如果release()之前mutex处于lock()状态,那必须自己管理并unlock()
unique_lock<mutex>l2(mymutex);
mutex *p = l2.release(); //不用自己unlock
l1.push_back(i); //假设i为收到的信息
p->unlock();
有时候会把锁住代码的多少称为锁的粒度 用粗细来描述
所有权的传递:
unique_lock<mutex>l2(mymutex); 此处mymutex的所有权属于l2
所有权可以转移但不能复制
unique_lock<mutex>l1(move(l2)); 现在l2指向空
移动构造函数:
unique_lock<mutex> setunique_lock()
{
unique_lock<mutex> lokk1(mymutex);
return lokk1;
}
unique_lock<mutex>l2 = setunique_lock();
返回这种局部对象会导致系统生成临时的unique_lock对象,并且调用unique_lock的移动构造函数
第七节:
设计模式:
代码的一些写法,程序灵活,维护起来可能比较方便。
用设计模式理念写出来的代码很晦涩。
单例设计模式:
整个项目中,有某个或某些特殊的类,属于该类的对象,只能创建一个。
单例类:
#include<iostream>
using namespace std;
#if 0
单例模式下,类的构造函数是私有的 以本代码为例
你不能直接通过MyGas a;来构造一个类对象,你必须通过接口函数GetUnstance()
这样的话一个特殊类只能创建一个对象 你二次调用GetUnstance()时返回的依然是第一次创建的指针
而类中嵌套类的方法,可以通过生成第二类的静态对象的析构函数来清除创建指针所用的空间 这样不会造成内存泄露 是一个不错的方式
#endif // 0
class MyCas //这是一个单例类
{
private:
MyCas() {} //私有化的构造函数
private:
static MyCas* m_instance;
public:
static MyCas* GetInstance()
{
if (m_instance == nullptr)
{
m_instance = new MyCas();
static CGarhuishou cl;
}
return m_instance;
}
class CGarhuishou //用来释放对象
{
public:
~CGarhuishou() //类的析构函数中清除对象
{
if (MyCas::m_instance)
{
delete MyCas::m_instance;
MyCas::m_instance = nullptr;
}
}
};
void func()
{
cout << "测试" << endl;
}
};
//类静态变量初始化
MyCas* MyCas::m_instance = NULL;
int main()
{
MyCas* p_a = MyCas::GetInstance(); //创建一个对象,返回该类对象的指针
MyCas* p_b = MyCas::GetInstance(); //创建的是同一个指针 p_a 跟 p_b 其实是同一个
p_a->func();
MyCas::GetInstance()->func(); //通过这种方式调用func也可以
}
单例设计模式共享数据问题分析、解决:
if (m_instance == nullptr) //双重锁定(双重检查) 效率提高
{
unique_lock<mutex>myuniquemutex(mymutex); //自动加锁
if (m_instance == nullptr)
{
m_instance = new MyCas();
static CGarhuishou cl;
}
}
return m_instance;
std::call_once():
c++11引入的函数 该函数的第二个参数是一个函数名a();
功能是能够保证函数a只被调用一次。
具备互斥量的能力,效率上又比互斥量消耗的资源更少。
call_once()只需要跟一个标记(std::once_flag)结合使用,std::once_flag其实是一个结构
call_once()通过标记决定函数是否执行,调用call_once()成功后将标记设置为已调用状态
只要once_flag标记为以调用状态,那么函数就不会再被执行。
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
once_flag g_flag; //标记
class MyCas //这是一个单例类
{
static void CreateInstance() //只被调用1次
{
m_instance = new MyCas();
static CGarhuishou cl;
}
private:
MyCas() {} //私有化的构造函数
private:
static MyCas* m_instance;
public:
static MyCas* GetInstance()
{
std::call_once(g_flag, CreateInstance);
return m_instance;
}
class CGarhuishou //用来释放对象
{
public:
~CGarhuishou() //类的析构函数中清除对象
{
if (MyCas::m_instance)
{
delete MyCas::m_instance;
MyCas::m_instance = nullptr;
}
}
};
void func()
{
cout << "测试" << endl;
}
};
//类静态变量初始化
MyCas* MyCas::m_instance = NULL;
//线程入口函数
void mythread()
{
cout << "我的子线程执行" << endl;
MyCas* p_a = MyCas::GetInstance();
cout << "我的子线程执行完毕" << endl;
return;
}
int main()
{
thread myobj1(mythread);
thread myobj2(mythread);
myobj1.join();
myobj2.join();
}
第八节:
条件变量condition_variable、wait()、notify_one():
condition_variable是一个类,与条件相关
这个类需要和互斥量来配合工作,生成类对象使用
#include<iostream>
#include<thread>
#include<vector>
#include<list>
#include<mutex>
using namespace std;
#if 0
lock_guard<mutex> lg1(mymutex); 声明格式
lock_guard的构造函数执行了mutex::lock()
析构执行了unlock();
#endif // 0
class A
{
public:
//接受消息
void setX()
{
for (int i = 0; i < 10000; ++i)
{
cout << "setX执行 插入一个元素: " << i << endl;
lock_guard<mutex> lg1(mymutex);
l1.push_back(i); //假设i为收到的信息
v1.notify_one();//把wait唤醒
}
return;
}
//取出信息
void printX()
{
int commed = 0;
while (true)
{
unique_lock<mutex> s1(mymutex);
//如果第二个参数lambda表达式返回值是false 那么wait()将解锁互斥量,并堵塞到本行
//堵塞到其他某个线程调用notify_one()成员函数为止
//如果第二个参数返回值是true 那么wair()直接返回
//如果没有第二个参数,那么效果如同lambda表达式返回值是false一样
//wait唤醒后会继续不断尝试获取互斥量锁 如果无法获取就会卡在这里
v1.wait(s1, [this] {
if (!l1.empty())
return true;
return false;
});
commed = l1.front();
l1.pop_front();
s1.unlock();
}
}
private:
list<int> l1; //存放信息
mutex mymutex; //创建一个互斥量
condition_variable v1; //生成一个条件变量对象
};
int main()
{
A a;
thread mySet(&A::setX, &a);
thread myPrint(&A::printX, &a);
mySet.join();
myPrint.join();
return 0;
}
notify_all() : 所有的wait函数都被唤醒
第九节:
std::async详解:
std::async是一个函数模板,用来启动一个异步任务
启动之后返回一个std::future对象 std::future 是一个类模板
启动异步任务就是自动创建一个线程并开始执行对应线程的入口函数,返回的future对象里边含有线程入口函数返回的结果(线程返回的结果)。通过调用future的成员函数get()来获取结果。
局部函数:
#include<iostream>
#include<future>
using namespace std;
int mythread()
{
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
int main()
{
cout << "main start " << " main id = " << this_thread::get_id() << endl;
future<int> res = async(mythread); //创建一个线程并开始执行,绑定关系
cout << "continue...." << endl;
int def;
def = 0;
cout << res.get() << endl; //等待thread执行完取值 只能调用一次
cout << "main end " << endl;
return 0;
}
类的成员函数:
#include<iostream>
#include<future>
using namespace std;
class A
{
public:
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
};
int main()
{
A a;
int tmppar = 12;
cout << "main start " << " main id = " << this_thread::get_id() << endl;
future<int> res = async(&A::mythread,&a,tmppar);
cout << "continue...." << endl;
int def;
def = 0;
cout << res.get() << endl; //等待thread执行完取值 只能调用一次
cout << "main end " << endl;
return 0;
}
asyuc()的其他参数:
通过额外向std::async()传递一个参数,这个参数是std::launch类型(枚举类型),来达到特殊的目的。
std::launch::deferred : 表示该线程的入口函数调用被延迟到future的wait或get函数调用时才执行
如果没有wait或者get函数被调用,那么线程甚至不会被创建。
std::launch::deferred 延迟调用,并且没有创建新线程 而是在主线程中调用
#include<iostream>
#include<future>
using namespace std;
class A
{
public:
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
};
int main()
{
A a;
int tmppar = 12;
cout << "main start " << " main id = " << this_thread::get_id() << endl;
future<int> res = async(launch::deferred,&A::mythread,&a,tmppar);
cout << "continue...." << endl;
int def;
def = 0;
cout << res.get() << endl; //等待thread执行完取值 只能调用一次
cout << "main end " << endl;
return 0;
}
结果: 线程的id一致
launch::async:
在调用async函数的时候就开始创建线程并且执行
async()函数默认使用launch::async这个标记
std::packaged_task :
打包任务 把任务包装起来
是个类模板 它的模板参数是各种可调用参数
通过std::package_task来把各种可调用对象包装起来 方便作为线程入口函数来调用
#include<iostream>
#include<future>
using namespace std;
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
int main()
{
int tmppar = 12;
cout << "main start " << " main id = " << this_thread::get_id() << endl;
std::packaged_task<int(int)>mypt(mythread);//把函数mythread通过packaged_task包装起来
thread myobj(ref(mypt),1); //线程开始执行 1是作为线程入口函数的参数
myobj.join();
future<int> res = mypt.get_future(); //绑定结果
cout << res.get() << endl;
cout << "main end " << endl;
return 0;
}
lamba表达式写法:
#include<iostream>
#include<future>
using namespace std;
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
int main()
{
std::packaged_task<int(int)>mypt([](int mypar) {
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
});
cout << "main start " << " main id = " << this_thread::get_id() << endl;
thread myobj(ref(mypt), 1); //线程开始执行 1是作为线程入口函数的参数
myobj.join();
future<int> res = mypt.get_future(); //绑定结果
cout << res.get() << endl;
cout << "main end " << endl;
}
也可以直接调用:
#include<iostream>
#include<future>
using namespace std;
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
int main()
{
std::packaged_task<int(int)>mypt([](int mypar) {
cout << mypar << endl;
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
});
cout << "main start " << " main id = " << this_thread::get_id() << endl;
mypt(777); //直接调用
future<int> res = mypt.get_future(); //绑定结果
cout << res.get() << endl;
cout << "main end " << endl;
}
结果:
id一致 都在主线程中
std::promise :
类模板 能够在某个线程中给他赋值 然后再其他线程中把这个值取出。
#include<iostream>
#include<future>
using namespace std;
#if 0
通过promise 保存一个值 将来某个时刻通过futureb绑定到promise 来获取这个值
#endif // 0
void mythread(promise<int> &tmpp , int calc)
{
calc++;
calc *= 10;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
int res = calc;
tmpp.set_value(res); //结果保存在tmpp中
return;
}
int main()
{
promise<int> myprom; //保存值类型为int
thread t1(mythread, ref(myprom), 777);
t1.join();
future<int> num = myprom.get_future();//获取结果
auto result = num.get();
cout << " result = " << result << endl;
cout << " end " << endl;
return 0 ;
}
创建新线程:
#include<iostream>
#include<future>
using namespace std;
#if 0
通过promise 保存一个值 将来某个时刻通过futureb绑定到promise 来获取这个值
#endif // 0
void mythread(promise<int> &tmpp , int calc)
{
calc++;
calc *= 10;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
int res = calc;
tmpp.set_value(res); //结果保存在tmpp中
return;
}
void mythread2(future<int>& tmpf)
{
auto result = tmpf.get();
cout << "thread 2 的 res = " << result << endl;
}
int main()
{
promise<int> myprom; //保存值类型为int
thread t1(mythread, ref(myprom), 777);
t1.join();
future<int> num = myprom.get_future();//获取结果
thread t2(mythread2, ref(num));
t2.join();
cout << " end " << endl;
return 0 ;
}
第十节:
future的其他成员函数:
#include<iostream>
#include<future>
using namespace std;
int mythread()
{
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
int main()
{
cout << "main start " << " main id = " << this_thread::get_id() << endl;
future<int> res = async(launch::deferred,mythread); //创建一个线程并开始执行,绑定关系
cout << "continue...." << endl;
future_status status= res.wait_for(chrono::seconds(6)); //枚举类型
//wait_for 等待一定的时间
if (status == future_status::timeout) //超时:等待一秒钟等待线程返回 未返回则超时
{
//表示线程未执行完毕
cout << "线程超时" << endl;
}
else if (status == future_status::ready)
{
//表示成功返回
cout << "线程成功执行完毕 返回" << endl;
cout << res.get() << endl;
}
else if (status == future_status::deferred) //延迟 如果async的第一个参数被设置为launch::deferred 成立
{
cout << "线程被延迟执行" << endl;
cout << res.get() << endl;
}
cout << " main end " << endl;
return 0;
}
shared_future:
#include<iostream>
#include<future>
using namespace std;
#if 0
get函数的底层是移动语义 调用一次之后就会置空 二次调用就会报错
那如果想要多次调用future的值
就需要shared_future
future中的get是移动数据 shared_future中的get是复制数据
#endif // 0
int mythread(int mypar)
{
cout << "mythread() start " << " this thread id = " << this_thread::get_id() << endl;
chrono::milliseconds dura(5000);
cout << "thread sleep" << endl;
this_thread::sleep_for(dura);
cout << "mythread() end " << " this thread id = " << this_thread::get_id() << endl;
return 5;
}
void mythread2(shared_future<int>& tmpf)
{
cout << "mythread2() start " << " this thread2 id = " << this_thread::get_id() << endl;
auto res = tmpf.get();
//get函数的设计是一个移动语义
cout << "mythread2 res = " << res << endl;
cout << " mythred2 end " << endl;
return;
}
int main()
{
cout << "main start " << " main id = " << this_thread::get_id() << endl;
std::packaged_task<int(int)>mypt(mythread);//把函数mythread通过packaged_task包装起来
thread myobj1(ref(mypt), 1); //线程开始执行 1是作为线程入口函数的参数
myobj1.join();
future<int> res = mypt.get_future(); //绑定结果
bool ifcanget = res.valid(); //判断future对象中的值是否有效值
shared_future<int> sh_res(move(res)); //转成右值
//shared_future<int> sh_res(res.share()); //第二种写法
ifcanget = res.valid();
thread myobj2(mythread2,ref(sh_res));
myobj2.join();
cout << "main end " << endl;
return 0;
}
int main()
{
cout << "main start " << " main id = " << this_thread::get_id() << endl;
std::packaged_task<int(int)>mypt(mythread);//把函数mythread通过packaged_task包装起来
thread myobj1(ref(mypt), 1); //线程开始执行 1是作为线程入口函数的参数
myobj1.join();
shared_future<int> res_s(mypt.get_future());//通过get_future()返回值直接构造shared_future对象
thread myobj2(mythread2, ref(res_s));
myobj2.join();
cout << "main end " << endl;
return 0;
}
原子操作atomic:
std::atomic 类模板 用来封装某个类型的值
#include<iostream>
#include<future>
using namespace std;
#if 0
原子操作理解为一种不需要用到互斥量加锁(无锁)技术的多线程并发编程方式
也可以理解为原子操作是多线程中不会被打断的程序执行片段
原子操作比互斥量效率更高 但互斥量加锁是针对代码段(较多) 原子操作一般针对一个变量
原子操作一般是指不可分割的操作 这种操作要么完成了要么未完成,不可能是半完成状态
std::atomic 类模板 用来封装某个类型的值
#endif // 0
atomic<int>g_mycount = 0; //封装了一个类型为int的对象
void mythread()
{
for (int j = 0; j < 10000; ++j)
{
g_mycount++; //对于的操作是原子操作 不会被打断
}
return;
}
int main()
{
thread myobj(mythread);
thread myobj2(mythread);
myobj.join();
myobj2.join();
cout << "最终结果 = " << g_mycount << endl;
}
#include<iostream>
#include<future>
using namespace std;
atomic<bool> g_ifend = false;
void mythread()
{
chrono::milliseconds dura(1999);
while (g_ifend == false)
{
//系统没要求线程退出 所以本线程可以干自己的事情
cout << " thrad id = " << this_thread::get_id() << " 运行 " << endl;
this_thread::sleep_for(dura);
}
cout << " thrad id = " << this_thread::get_id() << " 运行结束 " << endl;
return;
}
int main()
{
thread myobj(mythread);
thread myobj2(mythread);
chrono::milliseconds dura(5999);
this_thread::sleep_for(dura);
g_ifend = true;
myobj.join();
myobj2.join();
cout << " main end " << endl;
}
心得:
一般用于计数和统计(累计发送出去多少个数据包,累计收到多少个数据包)
第十一节:
std::async 深入研究:
若async不带参数 那么默认是launch::async | launch::deferred
系统会自行决定使用异步还是同步运行
thread 跟 async 的区别:
如果线程有返回值, 用thread创建时不容易拿到返回值。
async则容易拿到。
如果thread创建线程太多 则可能创建失败 系统容易崩溃
系统资源紧张的时候,用async这种不加额外参数的调用就不会参加新线程
那么这个异步任务就运行在执行get语句所在的线程上