在之前的代码中,我们并没有讨论 子线程的返回值问题。
这一章就考虑这个问题怎么处理。
下面我们先按照之前的写法,我们需要返回值时的可能的fix方案。
//如果线程有返回值,并且主线程要得到这个返回值,
//方案一:使用全局变量去接这个子线程的返回值。然后在main函数中取。
//如果线程有返回值,并且主线程要得到这个返回值,
//方案一:使用全局变量去接这个子线程的返回值。然后在main函数中取。
int teacher176readfuncreturnnumber = -1;
int teacher176writefuncreturnnumber = -1;
class Teacher176 {
public:
mutex mymutex;
list mylist;
public:
int readfunc(string &tempstr) {
for (size_t i = 0; i < 10; i++)
{
mymutex.lock();
cout << "read func tempstr = " << tempstr << " i = " << i << " threadid = " << this_thread::get_id()<< " &tempstr = " << &tempstr << " tempstr = " << tempstr << endl;
mymutex.unlock();
}
int tempreturnvalue = 10;
teacher176readfuncreturnnumber = tempreturnvalue;
return tempreturnvalue;
}
int writefunc(double &tempdouble) {
for (size_t i = 0; i < 10; i++)
{
mymutex.lock();
cout << "write func tempdouble = " << tempdouble << " i = " << i <<" threadid = " << this_thread::get_id() << " &tempdouble = " << &tempdouble <<" tempdouble = " << tempdouble << endl;
mymutex.unlock();
}
tempdouble = tempdouble * 2;
teacher176writefuncreturnnumber = (int)tempdouble;
return (int)tempdouble;
}
};
void main() {
cout << "main thread start " << endl;
Teacher176 tea;
string name = "nihao";
double dou = 60.8;
cout << "main threadid = " << this_thread::get_id() << " &name = " << &name << " name = " << name << " dou = " << dou << " &dou = " << &dou << endl;
thread readthread(&Teacher176::readfunc,&tea, ref(name));//注意要第二个传递的 &tea,不会调用copy构造函数。注意第三个参数,如果不用ref包裹,那么传递的也是copy,即使readfunc的参数是引用,thread内部也会搞一个copy,且readfunc的参数必须是const的。
thread writethread(&Teacher176::writefunc, &tea, ref(dou));
readthread.join();
writethread.join();
cout << "teacher176readfuncreturnnumber = " << teacher176readfuncreturnnumber << " teacher176writefuncreturnnumber = " << teacher176writefuncreturnnumber << endl;
cout << "main thread end" << endl;
}
从上述代码中,虽然可以获得子线程的返回值,但是这样并不方便。
C++给考虑到这种case,提供了 async函数,返回值是 future的方案。
在标头 |
async 是一个函数模版,用来启动一个异步任务。
注意这个异步任务可能是在子线程中启动,也可能不是在子线程中启动。这由 async的参数决定
//(C++11 起)(C++17 前)
//template< class Function, class... Args>
//std::future(std::decay_t...)>> async(Function&& f, Args&&... args);
从C++文档上来看,返回值是个类对象-future,
std::future
这个这个future里面有方法可以获得 返回值。
(构造函数) |
构造 future 对象 (公开成员函数) |
(析构函数) |
析构 future 对象 (公开成员函数) |
operator= |
移动future对象 (公开成员函数) |
share |
从 *this 转移共享状态给 shared_future 并返回它(公开成员函数) |
获取结果 |
|
get |
返回结果 (公开成员函数) |
状态 |
|
valid |
检查 future 是否拥有共享状态 (公开成员函数) |
wait |
等待结果变得可用 (公开成员函数) |
wait_for |
等待结果,如果在指定的超时间隔后仍然无法得到结果,则返回。 (公开成员函数) |
wait_until |
等待结果,如果在已经到达指定的时间点时仍然无法得到结果,则返回。 (公开成员函数) |
如下两行代码功能相同
future
future
意思是:
会启动一个异步任务,这个异步任务有可能在子线程中发生,也有可能是在调用这段code的线程中发生(在当前code中是在主线程发生)。
那么为什么C++编译器要这么干呢?这是因为在创建一个thread的时候,有可能会创建thread失败,如果创建thread失败,系统直接就崩溃了。
但是如果是async,会根据当前系统的繁忙程度,决定到底要不要create 一个线程来完成这个异步任务。
class Teacher177 {
public:
mutex mymutex;
list mylist;
public:
int readfunc(string &tempstr) {
for (size_t i = 0; i < 10; i++)
{
mymutex.lock();
cout << "read func tempstr = " << tempstr << " i = " << i << " threadid = " << this_thread::get_id() << " &tempstr = " << &tempstr << " tempstr = " << tempstr << endl;
mymutex.unlock();
}
return 10;
}
int writefunc(const int &tempdouble) {
for (size_t i = 0; i < 10; i++)
{
mymutex.lock();
cout << "write func tempdouble = " << tempdouble << " i = " << i << " threadid = " << this_thread::get_id() << " &tempdouble = " << &tempdouble << " tempdouble = " << tempdouble << endl;
mymutex.unlock();
}
//tempdouble = tempdouble * 2;
return (int)tempdouble;
}
public:
Teacher177() {
cout << "Teacher177 构造方法被执行" << endl;
}
~Teacher177() {
cout << "Teacher177 析构方法被执行" << endl;
}
Teacher177(const Teacher177& tea) {
cout << "Teacher177 copy 构造 被执行" << endl;
}
};
void main() {
Teacher177 tea;
string name = "nihao";
int dou = 300;
cout << "main threadid = " << this_thread::get_id() << " &name = " << &name << " name = " << name << " dou = " << dou << " &dou = " << &dou << endl;
future fu = async(std::launch::async | std::launch::deferred ,&Teacher177::readfunc, &tea, ref(name));
//future fu = async(&Teacher177::readfunc, &tea, ref(name));
future fu1 = async(std::launch::async | std::launch::deferred ,&Teacher177::writefunc, &tea, dou);
//future fu1 = async(&Teacher177::writefunc, &tea, dou);
cout<< fu.get() <
future
如果您的业务逻辑一定要在子线程完成,那么请清晰的加上 std::launch::async 这个标识符,当然,如果系统很忙的时候,当执行到这一行代码的时候,也会create thread,也会崩溃。
future
如果是deferred,表示会延迟启动这个异步任务。
那么延迟到什么时候启动这个任务呢?--延迟到使用结果 fu.get()的时候,
而且通过代码实验,我们观察到,如果是deferred,不会在子线程启动异步任务,而是在 当前线程。
主线程threadid = 11008
其他两个异步任务的threadid也是11008
main threadid = 11008 &name = 0000003519CFF768 name = nihao dou = 300 &dou = 0000003519CFF7A4
read func tempstr = nihao i = 0 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 1 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 2 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 3 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 4 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 5 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 6 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 7 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 8 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
read func tempstr = nihao i = 9 threadid = 11008 &tempstr = 0000003519CFF768 tempstr = nihao
10
write func tempdouble = 300 i = 0 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 1 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 2 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 3 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 4 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 5 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 6 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 7 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 8 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
write func tempdouble = 300 i = 9 threadid = 11008 &tempdouble = 0000020FD3DC41E8 tempdouble = 300
300
endsss
Teacher177 析构方法被执行