当前位置: 首页 > article >正文

C++学习笔记(19)

二、线程资源的回收
虽然同一个进程的多个线程共享进程的栈空间,但是,每个子线程在这个栈中拥有自己私有的栈空间。所以,
线程结束时需要回收资源。
回收子线程的资源有两种方法:
1)在主程序中,调用 join()成员函数等待子线程退出,回收它的资源。如果子线程已退出,join()函
数立即返回,否则会阻塞等待,直到子线程退出。
2)在主程序中,调用 detach()成员函数分离子线程,子线程退出时,系统将自动回收资源。分离后
的子线程不可 join()。
用 joinable()成员函数可以判断子线程的分离状态,函数返回布尔类型。
示例:
#include <iostream>
#include <thread> // 线程类头文件。
#include <windows.h> // Sleep()函数需要这个头文件。
using namespace std;
// 普通函数。
void func(int bh, const string& str) {
for (int ii = 1; ii <= 10; ii++)
{
cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << endl;
Sleep(1000); // 休眠 1 秒。
}
}
int main()
{
// 用普通函数创建线程。
thread t1(func, 3, "我是一只傻傻鸟。");
thread t2(func, 8, "我有一只小小鸟。");
t1.detach(); t2.detach(); // 分离子线程。
//cout << "任务开始。\n";
//for (int ii = 0; ii < 12; ii++) {
// cout << "执行任务中......\n";
// Sleep(1000); // 假设执行任务需要时间。
//}
//cout << "任务完成。\n";
//t1.join(); // 回收线程 t1 的资源。
//t2.join(); // 回收线程 t2 的资源。
Sleep(12000);
}
三、this_thread 的全局函数
C++11 提供了命名空间 this_thread 来表示当前线程,该命名空间中有四个函数:get_id()、sleep_
for()、sleep_until()、yield()。
1)get_id()
thread::id get_id() noexcept;
该函数用于获取线程 ID,thread 类也有同名的成员函数。
2)sleep_for() VS Sleep(1000) Linux sleep(1)
template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep,Period>& rel_time);
该函数让线程休眠一段时间。
3)sleep_until() 2022-01-01 12:30:35
template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);
该函数让线程休眠至指定时间点。(可实现定时任务)
4)yield()
void yield() noexcept;
该函数让线程主动让出自己已经抢到的 CPU 时间片。
5)thread 类其它的成员函数
void swap(std::thread& other); // 交换两个线程对象。
static unsigned hardware_concurrency() noexcept; // 返回硬件线程上下文的数量。
The interpretation of this value is system- andimplementation- specific, and may not
be exact, but just an approximation. Note that this does not need to match the actualnumber of processors or cores avail
able in the system: A system can supportmultiple threads per processing unit, or restrict
the access to its resourcesto the program. If this value is not computable or well defined,the function returns 0. 示例:
#include <iostream>
#include <thread> // 线程类头文件。
using namespace std;
// 普通函数。
void func(int bh, const string& str) {
cout << "子线程:" << this_thread::get_id() << endl;
for (int ii = 1; ii <= 3; ii++)
{
cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << endl;
this_thread::sleep_for(chrono::seconds(1)); // 休眠 1 秒。
}
}
int main()
{
// 用普通函数创建线程。
thread t1(func, 3, "我是一只傻傻鸟。");
thread t2(func, 8, "我有一只小小鸟。");
cout << "主线程:" << this_thread::get_id() << endl;
cout << "线程 t1:" << t1.get_id() << endl;
cout << "线程 t2:" << t2.get_id() << endl;
t1.join(); // 回收线程 t1 的资源。
t2.join(); // 回收线程 t2 的资源。
}
四、call_once 函数
在多线程环境中,某些函数只能被调用一次,例如:初始化某个对象,而这个对象只能被初始化一次。
在线程的任务函数中,可以用 std::call_once()来保证某个函数只被调用一次。
头文件:#include <mutex>
template< class callable, class... Args >
void call_once( std::once_flag& flag, Function&& fx, Args&&... args );
第一个参数是 std::once_flag,用于标记函数 fx 是否已经被执行过。
第二个参数是需要执行的函数 fx。
后面的可变参数是传递给函数 fx 的参数。
示例:
#include <iostream>
#include <thread> // 线程类头文件。
#include <mutex> // std::once_flag 和 std::call_once()函数需要包含这个头文件。
using namespace std;
once_flag onceflag; // once_flag 全局变量。本质是取值为 0 和 1 的锁。
// 在线程中,打算只调用一次的函数。
void once_func(const int bh, const string& str) {
cout << "once_func() bh= " << bh << ", str=" << str << endl;
}
// 普通函数。
void func(int bh, const string& str) {
call_once(onceflag,once_func,0, "各位观众,我要开始表白了。");
for (int ii = 1; ii <= 3; ii++)
{
cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << endl;
this_thread::sleep_for(chrono::seconds(1)); // 休眠 1 秒。
}
}
int main()
{
// 用普通函数创建线程。
thread t1(func, 3, "我是一只傻傻鸟。");
thread t2(func, 8, "我有一只小小鸟。");
t1.join(); // 回收线程 t1 的资源。
t2.join(); // 回收线程 t2 的资源。
}
五、native_handle 函数
C++11 定义了线程标准,不同的平台和编译器在实现的时候,本质上都是对操作系统的线程库进行
封装,会损失一部分功能。
为了弥补 C++11 线程库的不足,thread 类提供了 native_handle()成员函数,用于获得与操作系统
相关的原生线程句柄,操作系统原生的线程库就可以用原生线程句柄操作线程。
示例:
#include <iostream>
#include <thread>
#include <pthread.h> // Linux 的 pthread 线程库头文件。
using namespace std;
void func() // 线程任务函数。
{
for (int ii=1;ii<=10;ii++)
{
cout << "ii=" << ii << endl;
this_thread::sleep_for(chrono::seconds(1)); // 休眠 1 秒。
}
}
int main()
{
thread tt(func); // 创建线程。
this_thread::sleep_for(chrono::seconds(5)); // 休眠 5 秒。
pthread_t thid= tt.native_handle(); // 获取 Linux 操作系统原生的线程句柄。
pthread_cancel(thid); // 取消线程。
tt.join(); // 等待线程退出。
}
 


http://www.kler.cn/a/309795.html

相关文章:

  • Java-Redisson分布式锁+自定义注解+AOP的方式来实现后台防止重复请求扩展
  • Chromium 中sqlite数据库操作演示c++
  • 【go从零单排】Mutexes互斥锁
  • 数据分析那些事儿——关于A/B实验
  • 壹连科技IPO闯关成功!连接器行业上市企业+1
  • 24/11/11 算法笔记 泊松融合
  • vue3路由基本使用
  • 283. 移动零(快慢指针)
  • Linux权限理解【Shell的理解】【linux权限的概念、管理、切换】【粘滞位理解】
  • 零基础考过软考信息系统项目管理师经验分享
  • H5依赖安装
  • 一、(JS)JS中鼠标事件-mouseenter、mouseleave和mouseover、mouseout区别
  • 使用Redis实现用户关注博客的推模式
  • Go 交叉编译
  • Jenkins部署若依项目
  • 开源 AI 智能名片 S2B2C 商城小程序中的全渠道供应策略
  • 深度学习张量变换操作利器 einops 基础实践
  • 消息中间件有哪些常见类型
  • sql刷题常用函数
  • 微博计算架构实战
  • 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树
  • 【车载开发系列】ParaSoft单元测试环境配置(三)
  • 【原创】java+springboot+mysql高校社团网系统设计与实现
  • 【数据库】MySQL-基础篇-多表查询
  • 赋值运算符
  • 用Kimi输出流程图