#include
#include class X
{
public:void f(){std::cout << "hello" << std::endl;}
private:};void oops()
{X my_x;std::thread t(&X::f,&my_x);t.join();
}int main()
{oops();std::cin.get();
}
#include
#include void do_something(int& i)
{++i ;
}struct func
{int& i;func(int& i_) :i(i_) {}//func(func& j) = delete;void operator()(){std::cout << "3.取新线程内部的可调用对象this指针:\n\t" << this << std::endl;std::cout << "4.取新线程内部的可调用对象的成员变量i地址:\n\t" << &i << std::endl;for(unsigned j=0;j<10000000;++j){ do_something(i);}}
};void oops()
{int some_local_state=0;std::cout << "1.取oops()函数作用域内的局部变量some_local_state地址:\n\t" << &some_local_state << std::endl;func my_func(some_local_state);std::cout << "2.取oops()函数作用域内的局部变量my_func地址:\n\t" << &my_func << std::endl;std::thread my_thread(my_func);my_thread.detach();// my_thread.join();
}int main()
{oops();std::cin.get();
}
编译运行,输出信息如图:
上图红色序号2和3的地址数值不同(可调用对象的内存地址),说明这两处不是同一个对象。
解析:std::thread my_thread(my_func) 在构造实例时,在新线程内部实例化了一个新的func类型对象,这个新的对象拷贝了my_func对象的内容(调用的拷贝构造函数),而不是引用my_func本体。
上图红色序号1和4的地址数值相同,说明这两处是同一个对象
解析:my_func的内部成员int &i是对some_local_state的引用,新线程内部实例化了一个新的func类型对象,拷贝了my_func的内容,这个新的func类型对象的内部成员int &i也是some_local_state引用。
注意: my_thread.detach() 明确了不等待新线程运行结束。于是oops()退出后,新线程有可能还在继续运行,do_something(i)的下一步调用,会访问已经销毁的局部变量some_local_state。
在std::thread对象销毁前,我们要确保已经调用了join()或detach()。通常,若要分离线程,在线程启动后调用detach()即可,这不成问题。然后,假使打算等待线程结束,则需要小心地选择执行代码的位置来调用join()。原因是,如果线程启动后有异常抛出,而join()尚未执行,则该join()调用会被略过。
RAII,异常安全(exception-safe)C++ 标准保证了当异常发生时,会调用已创建对象的析构函数。
运用RAII机制确保新线程在函数f()退出前终结,在 ~thread_guard() 中调用 join() 时,先调用 joinable() 判断新线程能否汇合,因为在任何执行线程上 join() 只能被调用一次,如果线程已经汇合过,再次调用 join() 则是错误行为。
#include class thread_guard
{std::thread& t;
public:explicit thread_guard(std::thread& t_):t(t_){}~thread_guard(){if(t.joinable()){t.join();}}thread_guard(thread_guard const&)=delete;thread_guard& operator=(thread_guard const&)=delete;
};void do_something(int& i)
{++i;
}struct func
{int& i;func(int& i_):i(i_){}void operator()(){for(unsigned j=0;j<1000000;++j){do_something(i);}}
};void do_something_in_current_thread()
{throw - 1;
}void f()
{int some_local_state;func my_func(some_local_state);std::thread t(my_func);thread_guard g(t);do_something_in_current_thread();
}int main()
{f();
}
调用std::thread对象的成员函数detach(),会令线程在后台运行,遂无法与之直接通信。假若线程被分离,就无法等待它完结,也不可能获得与它关联的std::thread 对象,因而无法汇合该线程。然后分离的线程确实仍在后台运行,其归属权和控制权都转移给了C++运行时库,由此保证,一旦线程退出,与之关联的资源都会被正确回收。
UNIX操作系统中,有些进程叫做守护进程,它们在后台运行且没有对外的用户界面;沿袭这一概念,分离出去的线程常常被成为守护线程。这种线程往往长时间运行。几乎在应用程序的整个生命周期内,它们一直运行,以执行后台任务,如文件系统监控,从对象缓存中清除无用数据项,优化数据结构等。
另一种模式,就是由分离线程执行"启动后即可自主完成"的任务;我们还能通过分离线程实现一套机制,用于确认线程完成运行。
注意只有joinable() 返回true时,才能调用detach() 分离线程。
错误的传参方式:
void f(int i, std::string const& s)
{std::cout << "代码本意,字符串s的内容应是\t" << "123" << std::endl;std::cout << "实际的能容是\t" << s << std::endl;
}void oops()
{char buffer[1024];sprintf_s(buffer,1024,"%d", 123);std::thread t(f,1,buffer);t.detach();
}int main()
{oops();std::cin.get();
}
编译运行,结果如图:
传入的内容是字符串"123",实际显示的是空白。
buffer是个局部数组指针,我们本来希望将buffer隐式转换成string对象,再将其作为函数参数,可惜转换未能及时发生,新线程的线程函数被调用时,oops()就已经退出了,buffer已经被销毁,继而引发了未定义行为。这是因为:std::thread的构造函数原样复制所提供的参数,并未令其转换成我们预期的参数类型。
在buffer传入std::thread的构造函数之前,就把它转换成string对象。
#include
#include void f(int i, std::string const& s)
{std::cout << "代码本意,字符串s的内容应是\t" << "123" << std::endl;std::cout << "实际的内容是\t" << s << std::endl;
}
void oops()
{char buffer[1024];sprintf_s(buffer,1024,"%d", 123);std::thread t(f,1,std::string(buffer));t.detach();
}int main()
{oops();std::cin.get();
}
运行结果如图:
需求:在新线程函数f()内部,改写oops()作用域内的对象,我们想要f()的参数是非const引用,将对象通过引用方式传入f(),这时候需要使用std::ref方法才能做到。
#include
#include void f(int i, std::string & s)
{s = "ok";
}void oops()
{std:: string s;std::thread t(f,1,std::ref(s));t.join();std::cout << "现在的string对象s的内容是:\t" << s << std::endl;
}int main()
{oops();std::cin.get();
}
运行结果如图:
#include
#include
#include
void f(std::unique_ptr ptr)
{std::cout << ptr->c_str() << std::endl;
}
void oops()
{std::unique_ptr ptr(new std::string("hello"));std::thread t(f,std::move(ptr));t.join();
}
int main()
{oops();std::cin.get();
}
void f1();
void f2();
1.创建线程1,并使之与t1关联,代码如下:
std::thread t1(f1);
std::thread t2 = std::move(t1);
t1 = std::thread(f2);
std::thread t3;
5.将t2相关联的线程1的归属权移交给t3,现在t2不再与任何线程相关联
t3 = std::move(t2);
6.t1关联着线程2,却试图将t3关联的线程1归属权转移给t1,错误代码如下:
t1 = std::move(t3);
std::thread test()
{void f();return std::thread(f);
}
std::thread test()
{void f();std::thread t = std::thread(f);return t;
}
void test(std::thread t);
void run()
{void f();test(std::thread t(f));std::thread t(f);test(std::move(t));
}
#include
#include
#include class scoped_thread
{std::thread t;
public:explicit scoped_thread(std::thread t_):t(std::move(t_)){if(!t.joinable())throw std::logic_error("No thread");}~scoped_thread(){t.join();}scoped_thread(scoped_thread const&)=delete;scoped_thread& operator=(scoped_thread const&)=delete;
};void do_something(int& i)
{++i;
}struct func
{int& i;func(int& i_):i(i_){}void operator()(){for(unsigned j=0;j<1000000;++j){do_something(i);}}
};void do_something_in_current_thread()
{}void f()
{int some_local_state =0;scoped_thread t(std::thread(func(some_local_state)));do_something_in_current_thread();
}int main()
{f();
}
#include
#include
#include
#include void do_work(unsigned id)
{}void f()
{std::vector threads;for(unsigned i=0;i<20;++i){threads.push_back(std::thread(do_work,i));}std::for_each(threads.begin(),threads.end(),std::mem_fn(&std::thread::join));
}int main()
{f();
}
#include
#include
#include void foo()
{std::this_thread::sleep_for(std::chrono::seconds(1));
}int main()
{std::thread t1(foo);std::thread::id t1_id = t1.get_id();std::thread t2(foo);std::thread::id t2_id = t2.get_id();std::cout << "t1's id: " << t1_id << '\n';std::cout << "t2's id: " << t2_id << '\n';t1.join();t2.join();
}
#include
#include int main() {unsigned int n = std::thread::hardware_concurrency();std::cout << n << " concurrent threads are supported.\n";
}