thread 可以和 callable 类型一起工作。
#include <iostream>
#include <thread> //@ 使用多线程接口需要引入thread头文件
void hello() {
std::cout << "hello world from new thread" << std::endl;
}
int main()
{
std::thread t(hello); //@ 创建线程,指定线程入口函数
t.join(); //@ 等待线程执行完成
return 0;
}
thread t([] {std::cout << "hello world from new thread" << std::endl; });
t.join();
class FunObj
{
public:
void operator()(){ //@ 重载 operator ()
std::cout << "hello world from new thread" << std::endl;
}
};
int main()
{
FunObj fun_obj;
std::thread t(fun_obj);
t.join();
return 0;
}
class Greet
{
const char* owner = "New Thread";
public:
void SayHello(const char* name) {
std::cout << "Hello " << name << " from " << owner << std::endl;
}
};
int main()
{
Greet greet;
std::thread t(&Greet::SayHello, &greet, "C++ multi-threads");
t.join();
return 0;
}
也可以在类中重载 (),此时变为:
class Greet
{
const char* owner = "New Thread";
public:
void SayHello(const char* name) {
std::cout << "Hello " << name << " from " << owner << std::endl;
}
void operator() (const char* name){ //@ 重载 operator ()
std::cout << "Hello " << name << " from " << owner << std::endl;
}
};
int main()
{
Greet greet;
std::thread t(greet, "C++ multi-threads"); //@ 传参时的变化
t.join();
return 0;
}
当需要向线程传递参数时,可以直接通过 std::thread 的构造函数参数进行,构造函数通过完美转发将参数传递给线程函数。
void hello(std::string name) {
std::cout << "hello " << name << std::endl;
}
int main()
{
std::thread t(hello, "C++ 11 multi-threads");
t.join();
return 0;
}
class A
{
public:
int m_i;
//@ 类型转换构造函数,可以把一个int转换成一个类A对象。
A(int a) :m_i(a){
std::cout << "A::A(int a)构造函数执行!" << this <<
" thread id:" << std::this_thread::get_id() << std::endl;
}
A(const A &a) :m_i(a.m_i){
std::cout << "A::A(A &a)复制构造函数执行!" << this
<< " thread id:" << std::this_thread::get_id() << std::endl;
}
~A(){
std::cout << "A::~A()析构函数执行!" << this <<
" thread id:" << std::this_thread::get_id() << std::endl;
}
};
void myprint(A& pmybuf) //@ 使用引用作为参数,避免调用复制构造函数产生副本
{
pmybuf.m_i = 199;
std::cout << "子对象myprint的参数地址是 " << &pmybuf <<
" thread id: " << std::this_thread::get_id() << std::endl;// 打印的是pmybuf对象的地址
}
int main()
{
std::cout << "main thread id: " << std::this_thread::get_id() << std::endl;
A myobj(10); //@ 生成一个类对象
thread mytobj(myprint, std::ref(myobj));
mytobj.join();
std::cout << myobj.m_i << std::endl;
return 0;
}
void myprint(std::unique_ptr<int> pzn){
std::cout << *pzn << std::endl;
}
int main()
{
std::unique_ptr<int> myp(new int(100));
thread mytobj(myprint2, std::move(myp)); //@ 必须使用 move 否则报错
mytobj.join();
return 0;
}
一旦启动线程之后,我们必须决定是要等待直接它结束(通过join),还是让它独立运行(通过detach),必须二者选其一。如果在 thread 对象销毁的时候我们还没有做决定,则 thread 对象在析构函数中将调用 std::terminate() 从而导致我们的进程异常退出。
需要注意的是:在我们做决定的时候,很可能线程已经执行完了(例如上面的示例中线程的逻辑仅仅是一句打印,执行时间会很短)。
新的线程创建之后,究竟是新的线程先执行,还是当前线程的下一条语句先执行这是不确定的,因为这是由操作系统的调度策略决定的。
API | 注解 |
---|---|
join | 等待线程完成其执行 |
detach | 允许线程独立执行 |
joinable | 查询是否可以对该线程进行 join |
void loops()
{
for (int i = 0; i < 10; i++) {
std::cout << "this thread [" << std::this_thread::get_id()
<< " ] print:" << i << std::endl;
}
}
int main()
{
std::thread t(loops);
t.join();
std::cout << "main thrad exit" << std::endl;
return 0;
}
输出:
this thread [6244 ] print:0
this thread [6244 ] print:1
this thread [6244 ] print:2
this thread [6244 ] print:3
this thread [6244 ] print:4
this thread [6244 ] print:5
this thread [6244 ] print:6
this thread [6244 ] print:7
this thread [6244 ] print:8
this thread [6244 ] print:9
main thrad exit
void loops()
{
for (int i = 0; i < 10; i++) {
std::cout << "this thread [" << std::this_thread::get_id()
<< " ] print:" << i << std::endl;
}
}
int main()
{
std::thread t(loops);
t.detach();
std::cout << "main thrad exit" << std::endl;
return 0;
}
输出的结果是不定的,主线程退出以后,子线程不再输出到终端。
下面的函数都在 std::this_thread 命名空间中。
API | 注解 |
---|---|
yield | 让出处理器,重新调度各执行线程。通常用在自己的主要任务已经完成的时候,此时希望让出处理器给其他任务使用。 |
get_id | 返回当前线程的线程 id |
sleep_for | 使当前线程的执行停止指定的时间段 |
sleep_until | 使当前线程的执行停止直到指定的时间点 |
示例:
#include <iostream>
#include <thread>
#include <chrono>
#include <sstream>
#include <ctime>
#include <iomanip>
void print_time()
{
using std::chrono::system_clock;
auto in_time_t = system_clock::to_time_t(system_clock::now());
std::stringstream ss;
ss << std::put_time(localtime(&in_time_t), "%Y-%m-%d %X");
std::cout << "now is: " << ss.str() << std::endl;
}
void sleep_thread()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "[this thread: " << std::this_thread::get_id() <<
" ] is waking up:";
print_time();
std::cout << std::endl;
}
void wait_until_thread()
{
using std::chrono::system_clock;
time_t time = system_clock::to_time_t(system_clock::now());
struct std::tm *ptm = std::localtime(&time);
ptm->tm_sec += 10;
std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
std::cout << "[this thread: " << std::this_thread::get_id() <<
" ] waiting until: ";
print_time();
std::cout << std::endl;
}
void loop_thread()
{
for (int i = 0; i < 10; i++) {
std::cout << "[this thread: " << std::this_thread::get_id() <<
" ] print: " << i << std::endl;
}
}
int main()
{
print_time();
std::thread t1(sleep_thread);
std::thread t2(loop_thread);
std::thread t3(wait_until_thread);
t1.join();
t2.join();
t3.join();
return 0;
}
输出:
now is: 2020-04-04 11:07:57
[this thread: 14784 ] print: 0
[this thread: 14784 ] print: 1
[this thread: 14784 ] print: 2
[this thread: 14784 ] print: 3
[this thread: 14784 ] print: 4
[this thread: 14784 ] print: 5
[this thread: 14784 ] print: 6
[this thread: 14784 ] print: 7
[this thread: 14784 ] print: 8
[this thread: 14784 ] print: 9
[this thread: 12208 ] is waking up:now is: 2020-04-04 11:08:00
[this thread: 21040 ] waiting until: now is: 2020-04-04 11:08:07
API | 注释 |
---|---|
call_once | 即便在多线程环境下,也能保证只调用某个函数一次 |
once_flag | 与call_once 配合使用 |
在一些情况下,我们有些任务需要执行一次,并且我们只希望它执行一次,例如资源的初始化任务。这个时候就可以用到上面的接口。这个接口会保证,即便在多线程的环境下,相应的函数也只会调用一次。
示例:
有三个线程都会使用 init 函数,但是只会有一个线程真正执行它。
#include <mutex>
#include <thread>
#include <iostream>
void init() {
std::cout << "initializing..."<< std::endl;
}
void worker(std::once_flag* flag) {
std::call_once(*flag, init);
}
int main()
{
std::once_flag flag;
std::thread t1(worker, &flag);
std::thread t2(worker, &flag);
std::thread t3(worker, &flag);
t1.join();
t2.join();
t3.join();
return 0;
}
我们无法确定具体是哪一个线程会执行 init。而事实上,我们也不关心,因为只要有某个线程完成这个初始化工作就可以了。
原文:https://www.cnblogs.com/xiaojianliu/p/12632306.html