线程允许多个程序任务在统一时间执行,不同的线程可以共享内存空间,每个线程也有自己的栈空间。
本文章的代码库:
https://gitee.com/gamestorm577
线程类
thread
类thread表示单个执行线程。线程在thread构造对象时开始执行。每个thread对象表示唯一的一个线程,thread不支持复制构造和复制赋值函数。
构造函数
默认的构造函数构造一个不表示任何线程的thread对象:
thread() noexcept;
构造函数可以传入一个函数并关联一个执行线程,构造完成后函数将在该线程上开始运行:
template< class F, class... Args >
explicit thread( F&& f, Args&&... args );
代码示例:
auto func = [](int index)
{
for (int i = 0; i < 15; ++i)
{
std::cout << index;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};
std::thread t1(func, 1);
std::thread t2(func, 2);
t1.join();
t2.join();
std::cout << std::endl;
t1和t2分别在两个线程上同时执行,打印出来的1和2可能是交错的,可能的输出结果:
122121121212121212212121212121
析构函数
销毁thread对象时,必须合并或者分离底层线程,否则会调用std::terminate导致程序崩溃。其内部实现大致为:
~thread() noexcept
{
if (joinable())
{
terminate();
}
}
赋值函数
thread只支持移动赋值函数,移动后线程的所有权被转交,原thread对象不再表示任何线程。
joinable
检查线程是否可合并,如果thread代表的线程是活跃的,那么joinable返回true。代码示例:
std::cout << std::boolalpha;
std::thread t;
std::cout << "t joinable: " << t.joinable() << std::endl;
t = std::thread(
[]()
{
});
std::cout << "t joinable: " << t.joinable() << std::endl;
t.join();
std::cout << "t joinable: " << t.joinable() << std::endl;
输出结果:
t joinable: false
t joinable: true
t joinable: false
get_id
获取线程的id。代码示例:
std::thread t1;
std::thread t2 = std::thread(
[]()
{
});
std::thread t3 = std::thread(
[]()
{
});
std::cout << "t1 id: " << t1.get_id() << std::endl;
std::cout << "t2 id: " << t2.get_id() << std::endl;
std::cout << "t3 id: " << t3.get_id() << std::endl;
t2.join();
t3.join();
输出结果:
t1 id: 0
t2 id: 97068
t3 id: 73212
native_handle
获取底层实现的线程句柄。代码示例:
std::thread t = std::thread(
[]()
{
});
std::thread::native_handle_type handle = t.native_handle();
std::cout << "native handle: " << handle << std::endl;
t.join();
输出结果:
native handle: 00000000000000A8
hardware_concurrency
返回系统的逻辑处理线程数。代码示例:
auto num = std::thread::hardware_concurrency();
std::cout << "hardware concurrency: " << num << std::endl;
可能的输出结果:
hardware concurrency: 8
join
阻塞thread代表的线程直到其执行结束。代码示例:
auto Func = []()
{
std::cout << "t thread" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
};
std::thread t(Func);
t.join();
std::cout << "Done " << std::endl;
输出结果:
t thread
Done
detach
将thread管理的线程从thread中分离,thread不再代表任何线程。代码示例:
auto Func = []()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Func about to end" << std::endl;
};
std::thread t(Func);
t.detach();
std::cout << "t detach" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
输出结果:
t detach
Func about to end
swap、std::swap
交换两个thread管理的线程。
jthread
和thread类似,jthread管理一个执行线程。不同的是,当jthread析构时会自动合并线程。jthread内部还有一个stop_source成员(stop_source包含stop_token对象),jthread可以接受一个用stop_token作为首参数的函数用于执行线程访问,jthread有一个request_stop接口用于修改stop_token的状态,这允许执行函数根据stop_token的状态来决定是否终止函数运行。
构造函数
构造一个jthread对象并关联一个执行线程(默认构造函数不关联任何线程)。代码示例:
auto func = [](int index)
{
for (int i = 0; i < 15; ++i)
{
std::cout << index;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};
std::jthread t1(func, 1);
std::jthread t2(func, 2);
t1.join();
t2.join();
std::cout << std::endl;
输出结果:
122112121212122121211212211212
析构函数
jthread对象析构时,会尝试合并其管理的执行线程,其内部实现大致为:
~jthread()
{
if (joinable())
{
request_stop();
join();
}
}
赋值函数
jthread只支持移动赋值函数,移动后线程的所有权被转交,原jthread对象不再表示任何线程。
joinable
检查线程是否可合并,如果thread代表的线程是活跃的,那么joinable返回true。
get_id
获取线程的id。
native_handle
获取底层实现的线程句柄。
hardware_concurrency
返回系统的逻辑处理线程数。
join
阻塞thread代表的线程直到其执行结束。
detach
将jthread管理的线程从jthread中分离,jthread不再代表任何线程。
swap、std::swap
交换两个jthread管理的线程。
停止记号处理
jthread提供了get_stop_source、get_stop_token用于获取stop_source和stop_token对象,requset_stop接口用于修改stop_token状态为停止,jthread关联的执行函数可以通过其第一个参数stop_token用于其状态。代码示例:
auto Func = [](std::stop_token token)
{
for (int i = 0; i < 10; ++i)
{
if (token.stop_requested())
{
std::cout << i << " token stop_requested true" << std::endl;
}
else
{
std::cout << i << " token stop_requested false" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
std::jthread t(Func);
std::this_thread::sleep_for(std::chrono::milliseconds(350));
t.request_stop();
可能的输出结果:
0 token stop_requested false
1 token stop_requested false
2 token stop_requested false
3 token stop_requested false
4 token stop_requested true
5 token stop_requested true
6 token stop_requested true
7 token stop_requested true
8 token stop_requested true
9 token stop_requested true
注意最后一行代码
t.request_stop();
不是必须的,因为对象 t 在析构时也会调用request_stop函数。
当前线程管理函数
yield
重调度线程的执行,允许其他线程运行。具体效果依赖于编译器的实现。
get_id
返回当前线程的id。代码示例:
auto Func = []()
{
std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
};
std::thread t1(Func);
std::thread t2(Func);
Func();
t1.join();
t2.join();
输出结果:
thread id: 22388
thread id: 79692
thread id: 85280
sleep_for
阻塞当前线程一段时间。代码示例:
auto t1 = std::chrono::system_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(300));
auto t2 = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> time = t2 - t1;
std::cout << "time pass: " << time.count() << std::endl;
可能的输出结果:
time pass: 302.473
sleep_util
阻塞当前线程到指定的时间点。代码示例:
auto t1 = std::chrono::system_clock::now();
std::this_thread::sleep_until(t1 + std::chrono::milliseconds(400));
auto t2 = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> time = t2 - t1;
std::cout << "time pass: " << time.count() << std::endl;
可能的输出结果:
time pass: 400.504