没来得及整理~
//
// day1.cpp
// LearnC++MultipleCode
//
//
#include "day1.hpp"
#include
#include
/*
1. 线程管理
*/
namespace day1 {
//1. 构造方法生成 std::thread ,参数,可调用类型构造
//函数指针
void doSomething() {
std::cout << "get_id() = " << std::this_thread::get_id() << std::endl;
std::cout<< "day1 doSomething ....."<<std::endl;
}
//函数对象,提供的函数对象会复制到新线程的存储空间当中,函数对象的执行和调用都在线程的内存空间中运行
class DoSomethingElse {
public:
void operator()() {
std::cout<< "day1 Do Something Else ....."<<std::endl;
}
};
//lambda表达式
auto doSomethingMore = []() {
std::cout<< "day1 doSomething More ....."<<std::endl;
};
template<class Y>
class X {
public:
void do_lengthy_work(){
std::cout << "do_lengthy_work " << std::endl;
}
void do_lengthy_work1(int a){
std::cout << "do_lengthy_work , before a = " << a << std::endl;
a = 3;
std::cout << "do_lengthy_work , after a = " << a << std::endl;
}
};
class thread_gaurd {
public:
//书上加了explicit,但是不用好像也可以
thread_gaurd(std::thread &t):t_(t) {}
~thread_gaurd() {
if (t_.joinable()) {
std::cout << "t_.join();" << std::endl;
t_.join();
}
}
thread_gaurd(const thread_gaurd &tg) = delete;
thread_gaurd& operator=(const thread_gaurd &g) = delete;
private:
std::thread &t_;
};
void day1Test() {
std::thread t1(doSomething);
// std::thread t2((DoSomethingElse())); //注意⚠️:这里并不是构造一个临时对象,更像是一个函数声明,圆括号作为函数声明消除了歧义;这里相当与声明了一个名为t1的函数,这个函数带有一个参数(函数指针指向没有参数并返回background_task对象的函数),返回一个 std::thread 对象的函数,而非启动了一个线程。
DoSomethingElse e;
std::thread t2(e);
std::thread t3((DoSomethingElse()));
//这个时候是不是看出了初始化列表的好处呢?
std::thread t4{DoSomethingElse()};
std::thread t5(doSomethingMore);
/*
//注意⚠️:要学会分离或者等待,否则会抛出异常'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1817e23ec),如果 std::thread 对象销毁之前还没有做出决定,程序就会终止 ( std::thread 的析构函数会调用 std::terminate() )。
//注意⚠️: 因此,即便是有异常存在,也需要确保 线程能够正确的加入(joined)或分离(detached)。
*/
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
//注意⚠️: join只能一次,之后调用要用joinable()判断
//如何使得线程在即使有异常也能正常退出?使用RAII,Resource Acquisition Is Initialization 资源获取即初始化方式,包装余个类,在析构函数中使用join()
std::thread t6(doSomething);
thread_gaurd tg{t6};
std::thread t7(doSomething);
thread_gaurd tg1 = t7;
// thread_gaurd tg2 = std::thread(doSomething);//No viable conversion from 'std::thread' to 'day1::thread_gaurd' 为什么这个不可以???难道是因为引用的不能是个临时的值?
/*
分离, detach
*/
//注意⚠️: 判断一个线程可否detach的方式也是joinable(),join的线程不能再detach,且不能对未启动的线程join()或者detach,虽然听着不太可能
std::thread t8{[](){
std::cout << "t8 dosomething" << std::endl;
}};
t8.detach();
/*
线程传递参数
*/
//传递参数实际上就是需要拷贝到独立内存中。
//注意不要传递指针过去,你访问的对象可能会不存在了。
int arr[5]{1,2,3,4,5};
char *c{"1111111"};
std::thread t9{[](int size, int *arr){
std::cout<< "t9 wait a minite ~" << std::endl;
std::cout<< "t9 wait a minite ~" << std::endl;
std::cout<< "t9 wait a minite ~" << std::endl;
std::cout<< "t9 wait a minite ~" << std::endl;
std::cout<< "t9 wait a minite ~" << std::endl;
if (arr[size - 1] == 5) {
std::cout<< "a = "<< (size == 0 ? -1 : arr[size-1]) << std::endl;
} else {
std::cout<< "oh , you pass a pointer which is a arr, now it is abnormal !" << std::endl;
}
}, 5, arr};
t9.detach();
//程序会输出 oh , you pass a pointer which is a arr, now it is abnormal !
/*
传递一个参数是引用怎么做,实际上最好不要传引用,很危险......,我现在想实现
但是要知道线程只会盲目的拷贝值,所以你传递一个变量,实际上是副本,所以要用std::ref
*/
int a = 0;
std::thread t10{[](int &a) {
std::cout<< "hh , a is passed as a ref, a = " << a << " and, i will change it to 10000000. " << std::endl;
a = 10000000;
}, std::ref(a)};
t10.join();
std::cout<< "hh , after join t10 a = " << a << std::endl;
/*
hh , a is passed as a ref, a = 0 and, i will change it to 10000000.
hh , after join t10 a = 10000000
*/
/*
还可以传递对象成员函数,对象地址作为指针对象 提供给函数
*/
std::list<int> lst1{7,2,6,1,2,3,4,5};
std::list<int> *list_ptr = &lst1;
// list_ptr->sort();
std::thread t11{&std::list<int>::clear, list_ptr};
//为什么下面方法不行?
// std::thread t13{&std::list::sort, list_ptr};
t11.join();
// t13.join();
std::cout << "lst1 = " << std::endl;
for (auto it = lst1.begin(); it != lst1.end(); ++it) {
std::cout << *it << std::endl;
}
X<int> my_x;
std::thread t12(&X<int>::do_lengthy_work,&my_x); // 1
t12.join();
X<int> my_x1;
int a1 = 1;
std::thread t14(&X<int>::do_lengthy_work1,&my_x1, a1); // 1
t14.join();
std::cout << "a1 = " << a1 <<std::endl;
/*
转移线程所有权
*/
/* 首先要理解的是,为什么std::thread是管理一个线程?
1. 它只能管理一个线程
2. 它不能被拷贝,也就是它管理的线程不能被拷贝
3. 我们所使用的移动拷贝构造实际上是把线程交给了别人,当前线程的管理者没事做了,又可以去管理其他线程
4. 但是注意如果你正在管理一个线程,那么你不能再管理其他线程,看看下面例子
*/
std::thread t21(doSomething);
std::thread t22{std::move(t21)};
//也就是说可以赋值,但前提是thread当前没有管理线程
t21 = std::thread(doSomething);
// t21 = std::move(t22);
/*
挂了
thread& operator=(thread&& __t) _NOEXCEPT {
if (!__libcpp_thread_isnull(&__t_))
terminate(); //挂在了这里
__t_ = __t.__t_;
__t.__t_ = _LIBCPP_NULL_THREAD; //这里会置为空线程?
return *this;
}
*/
t21.join();
t22.join();
/*
同样也就可以作为函数参数传递了,用移动拷贝
*/
auto createAThread = []()-> std::thread {
std::cout << "createAThread" << std::endl;
std::thread t(doSomething);
// return std::move(t);//Moving a local object(局部对象) in a return statement prevents copy elision.copy elision(就是不copy了,直接在函数调用的地方构造), 这个对象会默认采用移动拷贝给t23!。
return t;
};
std::thread t23 = createAThread();
std::cout << "t23::get_id()1 = " << t23.get_id() << std::endl;
t23.join();
std::thread t24 = std::move(t23);
std::cout << "std::this_thread::get_id() = " << std::this_thread::get_id() << std::endl;
// t24.join(); //报错,此时其实t23yijoinable,不能再joinable了,故你在接受移动过来的对象线程时一定要判断是否可以joinable()
/*
运行时决定线程数量std::thread::hardware_concurrency()
线程标识类型为 std::thread::id
第一种,可以通过调 用 std::thread 对象的成员函数 get_id() 来直接获取。如果 std::thread 对象没有与任何执 行线程相关联, get_id() 将返回 std::thread::type 默认构造值,这个值表示“无线程”。
第二 种,当前线程中调用 std::this_thread::get_id() (这个函数定义在 头文件中)也可以 获得线程标识。
*/
/*
day1 Do Something Else .....day1 doSomething .....day1 doSomething More .....
day1 Do Something Else .....
day1 Do Something Else .....
day1 doSomething .....
day1 doSomething .....
t8 dosomething
t_.join();
t9 wait a minite ~t_.join();
t9 wait a minite ~
t9 wait a minite ~
t9 wait a minite ~
t9 wait a minite ~
oh , you pass a pointer which is a arr, now it is nullptr !
main ()
*/
}
}