join()
- 进程是资源分配的最小单位
- 线程共享进程的栈空间,但是每个线程拥有独立的栈
- 主程序调用join目的是
等待子线程退出,回收他的资源
如果子线程已经推出,join、立即执行
如果没有退出,join阻塞,直到子线程退出
注意
不能同时对一个线程使用join和detach- 格式
thread my(show); my.join();
detach()
- 主程序调用detach目的:
分离子线程,
子线程退出时系统才自动回收资源- 注意
不能同时对一个线程使用join和detach- 另外可以通过延迟[sleep]主程序return
来查看detach执行情况
如果不sleep可能看不到结果,
但是不影响程序后台执行
joinable()
bool类型函数,它会表示当前的线程是否是可执行线程(能被join或者detach)
1 -> 表示可以join或者detach
0 ->表示已经可以join或者detach不可再重复回收资源
this_thread全局函数
this_thread::get_id()
获取线程ID
void fun(int ret, const string & str) {
cout << "我是第" << ret << "个成员,我的线程ID是: ";
cout << this_thread::get_id() << endl;
}
int main() {
thread my(fun, 10, "哈啊哈哈");
my.join();
thread my1(fun, 11, "wssdasda哈");
cout << "我是主线程,我操作的my1的子线程ID是:" << my1.get_id() << endl;
my1.join();
cout << "我是主线程,我操作的my1的子线程ID是:" << my1.get_id();
}
第二次my1的线程id是0是因为他已经被回收
this_thread::sleep_for()
线程休眠
时间参数是休眠的具体时间
//方法1
chrono::milliseconds dura(1000);
this_thread::sleep_for(dura);
//方法2
this_thread::sleep_for(chrono::milliseconds(100));
this_thread::sleep_until()
时间参数是时间点【略】
this_thread::yield()
让线程放出自己抢到的时间片
swap(不属于this_thread命名空间下)
格式(二者选其一)
swap(a,b);
a.swap(b);
移动构造函数拷贝进程[不在this::thread]
thread类的拷贝函数被删除
所以用移动构造函数赋值
另外赋值的已经转移,原来的线程付给了新的
原来的废弃,新的接管
如下图my不可再用,my1接管my
使用move可以转为右值
创建线程
void test01();//函数作为卡调用对象传入
void test02();//类作为可调用对象传入
void test03();//lambda匿名函数
普通函数创建线程
void fun(int ret, const string& str) {
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
}
int main() {
thread my(fun, 10, "哈啊哈哈");
my.join();
}
lambda匿名函数类创建线程
auto f = [](int ret, const string& str) {
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
};
thread my(f, 10, "哈啊哈哈");
my.join();
//注意default和传入值
thread my1([](int ret = 10, const string &str = "啊啊啊啊啊") {
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
},50,"aaaaaa");
my1.join();
类创建线程
仿函数创建线程
切记仿函数无参不创建匿名类对象【原因不详】
#include <iostream>
#include <thread>
using namespace std;
class stu {
public:
void operator()(int ret, const string& str);
};
void stu::operator()(int ret, const string& str)
{
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
}
int main() {
stu s1;
thread name(s1, 20, "哈哈哈");
name.join();
//注意用仿函数类名对象,必须带参数,如果无参只能用上面的不能用stu()
thread name1(stu(), 30, "aaa");
name1.join();
}
类的静态成员函数创建线程
#include <iostream>
#include <thread>
using namespace std;
class stu {
public:
static void fun(int ret, const string& str);
};
void stu::fun(int ret, const string& str)//类成员函数类外写
{
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
}
int main() {
thread name(stu::fun, 3, "hello");
name.join();
}
类的普通成员函数【注意】
①、必须先创建对象
②、对象的生命周期比子线程长
③、注意传进去的是指针
thread name(&stu::fun,&s1, 3, “hello”);
#include <iostream>
#include <thread>
using namespace std;
class stu {
public:
void fun(int ret, const string& str);
};
void stu::fun(int ret, const string& str)//类成员函数类外写
{
cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
}
int main() {
stu s1;
thread name(&stu::fun,&s1, 3, "hello");
name.join();
}
【创建线程总结之万变不离其宗】
普通函数、lambda、static类成员函数
thread thread_name(方法,参数1,参数2 , ... ,参数n)
仿函数类
thread thread_name(类对象,参数1,参数2 , ... ,参数n)
注意如果是匿名类对象(stu())则必须有参数,无参的可使用匿名类对象
普通成员函数
thread thread_name(方法,类对象(不可匿名),参数1,参数2 , ... ,参数n)
相关代码
#include <iostream>
using namespace std;
//thread是一个类
#include <thread>//线程头文件linux和win
//show函数
void show(){
cout<<"我是show函数子线程1"<<endl;
cout<<"我是show函数子线程2"<<endl;
cout<<"我是show函数子线程3"<<endl;
cout<<"我是show函数子线程4"<<endl;
cout<<"我是show函数子线程5"<<endl;
}
//stu类
class stu
{
public:
void operator()(){//方函数
cout<<"我是class类子线程1"<<endl;
cout<<"我是class类子线程2"<<endl;
cout<<"我是class类子线程3"<<endl;
cout<<"我是class类子线程4"<<endl;
cout<<"我是class类子线程5"<<endl;
}
};
void test01();//函数作为卡调用对象传入
void test02();//类作为可调用对象传入
void test03();//lambda匿名函数
int main(){
//test01();
//test02();
test03();
cout<<"我是主线程1"<<endl;
cout<<"我是主线程2"<<endl;
return 0;
}
void test03(){
//()无参数可以省略小括号
thread my_xc([](){
cout<<"我是lambda匿名函数子线程1"<<endl;
cout<<"我是lambda匿名函数子线程2"<<endl;
cout<<"我是lambda匿名函数子线程3"<<endl;
cout<<"我是lambda匿名函数子线程4"<<endl;
cout<<"我是lambda匿名函数子线程5"<<endl;
});
my_xc.join();
}
void test02(){
stu s1;
thread my_xc(s1);
//my_xc.join();
//detach个走个的,输出顺序会混乱
my_xc.detach();//注意join后不可以detach,二者只能有一个
}
void test01(){
//子线程也从函数开始执行(初始函数)(初始函数结束则线程结束)
thread my_xc(show);//show[函数是一个可调用对象
//join-->阻塞主线程,让主线程等待子线程执行完毕,会合后一起走
//注释掉join或放到return 0 后面
//报错terminate called without an active exception
//my_xc.join();//主线程被堵住子线程依然可以走
//joinable可判断是否join、detach过
cout<<"是否可以join或detach:"<<my_xc.joinable()<<endl;
//主线程从mian函数开始(一般主线程结束子线程就会被强行结束[detach例外])
//主线程和子线程没关系可以用detach【注意join之后不可再detach】
my_xc.detach();//【守护线程】(子线程在后台运行)【但是不建议用detach】
cout<<"是否可以join或detach:"<<my_xc.joinable()<<endl;
}
使用detach格外小心
传递int类型时不建议用引用(detach)
如果传递类对象要进行转换,不要让编译器进行隐式转换
【使用临时对象可以确保线程参数再mian函数结束之前就被构造出来】
thread my(show1,i,string(buff));
thread my(show1,i,mydelfclass(buff));
#include <iostream>
using namespace std;
//thread是一个类
#include <thread>//线程头文件linux和win
//类
class stu{
public:
int m;
stu(int m):m(m){
cout<<"stu的构造函数id:"<<this_thread::get_id()<<endl;
}
stu(const stu &s){
cout<<"stu的拷贝函数"<<endl;
}
~stu(){
cout<<"stu的西沟函数"<<endl;
}
};
//show函数
void show(const int &i ,const string &mybuff){
cout<<i<<endl;
cout<<mybuff<<endl;
}
//class_show
void show_class(const int &i ,const stu &my_stu){
cout<<&my_stu<<endl;
}
void test01();
void test02();
void test03();
int main(){
//test01();
test02();
cout<<"我是主线程1:"<<this_thread::get_id()<<endl;
cout<<"我是主线程2:"<<this_thread::get_id()<<endl;
return 0;
}
void test02(){
int i = 1;
int arr = 12;
thread my(show_class,i,stu(arr));
my.detach();//但是detach会存在问题【不推荐引用,但是指针一定不可以】
}
void test01(){
int i = 1;
int &arr = i;
char buff[] = "我是子线程";
//string(buff)对这个对象进行转【临时的string对象】
thread my(show,i,string(buff));
//my.join();//show用的是i是thread的拷贝的新地质
my.detach();//但是detach会存在问题【不推荐引用,但是指针一定不可以】
}
趣味练习 之 线程排序
sleep排序法
#include <iostream>
#include <thread>
#include <windows.h>
#include <mutex>
#include <vector>
#include <future>
using namespace std;
vector <double> res;
mutex flag;
void show(const double ret) {
Sleep(ret*1000);//排序的数字越细,*的数字要越大
flag.lock();
res.push_back(ret);
cout << "我是" << ret << "我排序完毕" << endl;
//cout << "我的线程ID:" << this_thread::get_id() << endl;
flag.unlock();
}
int main() {
vector <double>ret = { 10,8,20,11,21,21.001};
thread n1(show, ret[0]);
thread n2(show, ret[1]);
thread n3(show, ret[2]);
thread n4(show, ret[3]);
thread n5(show, ret[4]);
thread n6(show, ret[5]);
n1.join();
n2.join();
n3.join();
n4.join();
n5.join();
n6.join();
for (auto a : res)
cout << a << endl;
}
猴子排序法
#include <iostream>
#include <thread>
#include <windows.h>
#include <mutex>
#include <vector>
#include <future>
using namespace std;
vector <double> res;
mutex flag;
void show(const double ret) {
flag.lock();
res.push_back(ret);
cout << "我是:" << ret << endl;
flag.unlock();
}
int main() {
bool flag = 0;
a:
vector <double>ret = { 10,8,20,11,21,21.001};
thread n1(show, ret[0]);
thread n2(show, ret[1]);
thread n3(show, ret[2]);
thread n4(show, ret[3]);
thread n5(show, ret[4]);
thread n6(show, ret[5]);
n1.join();
n2.join();
n3.join();
n4.join();
n5.join();
n6.join();
for (int i = 0; i < res.size()-1; i++)
{
if (res[i] <= res[i + 1])
flag = 1;
else {
flag = 0;
res.clear();
cout << "排序不对,再进行新一轮排序" << endl;
goto a;
}
}
// if (flag == 1)
cout << "排序完毕"<<endl;
for (auto a : res)
cout << a << endl;
}