关于std::thread的封装引发的一系列问题及其解答

本文探讨了在C++中封装自定义线程的方法,包括如何正确传递成员函数到std::thread,解决this指针问题,以及避免不同编译器this指针位置差异带来的困扰。

某日突发奇想,想封装自己的thread,想要实现的效果类似java的thread

#include <thread>
#include <chrono>

using namespace std;

class mThread :public std::thread {
public:
	mThread() : thread(mThread::run){ this->detach();}

	virtual ~mThread() { is_live = false; }
private:
	void thread_callback();

	void run();

private:
	bool is_live;
};

void mThread::thread_callback() {
	cout << "mThread run" << endl;
}

void mThread::run() {
	is_live = true;
	while (is_live) {
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		thread_callback();
	}
}

int main() {
	mThread demo;

	int n;
	cin >> n;
	return 0;
}

(声明一下这里为什么不用仿函数,因为thread构造函数接受的是一个仿函数的对象而不是指针,仿函数作为参数传进去的是一个this解引用对象的一个拷贝,而不是本来的this)

这是我以为的代码写法
结果编译报错,原来类的成员函数的地址要用取地址符号,然后修改构造函数

mThread() : thread(&mThread::run){ this->detach();}

然后又报错了,原来他无法调用函数run,因为run函数不是void run(),而是void mThread::run(),再说详细一点就是

class mThread{
public:
	void run(){
		cout<<"run"<<endl;
	}
}
int main(){
	void (mThread::*demo1)() = &mThread::run;//这行能编译通过,
	void (*demo2)() = &mThread::run;//这行报错
}

然后我们如何以函数指针的方式调用这个run函数呢?
要这样

int main() {
	void (mThread::*demo1)() = &mThread::run;
	mThread a;
	(a.*demo1)();//间接调用编译通过

	demo1();//直接调用编译报错
}

看到了吧,我们只能通过一个a对象,来间接调用这个函数,而std::thread哪儿去给你找一个临时对象来调用这个函数呢,所以编译通不过。

那我们要怎么样才能让std::thread能正常的使用我们传进去的run函数呢?
这里普及一个小知识

class mThread {
public:
	void run() {
		cout << "run" << endl;
	}
};
int main(){
	void (mThread::*demo1)()  = null;//声明一个函数指针	demo1
	void (*demo2)(mThread *This) = null;//声明一个函数指针demo2
	demo2 = ((void (*)(mThread *))  (*(int*)&demo1));

	demo2(new mThread());
}

这时候能成功的输出run。

在这里虽然不能直接将demo1和demo2相互赋值,但是你应该明白的是,demo1和demo2实际上是同一个类型,只要你明白c++是怎么实现面向对象的,那么你就明白为什么我说他们俩是同一个类型。推荐一本书<深入探索c++模型>,懒得看书就看我下面写的

你看类的成员函数,能够调用类的对象的一些属性,同时能够修改对象的一些属性,拿他怎么知道修改的是那个对象的属性呢,当然是函数传了一个this指针进去啦

就比如这个run函数,你以为他是void run();其实他是 void run(mThread *this),最后一个参数是编译器帮你加上去的,所以说demo1和demo2实际上是同一个类型,只是在编译器哪里不是同一个类型而已。

所以现在可以将之前的构造函数改成这样

mThread() : thread(&mThread::run,this){ this->detach();}

然后就可以编译成功了,附加完整代码

#include <thread>
#include <chrono>

using namespace std;

class mThread :public std::thread {
public:
	mThread() : thread(&mThread::run,this,5){ this->detach();}

	virtual ~mThread() { is_live = false; }
public:
	void thread_callback() {
		cout << "mThread run" << endl;
	}

	void run(int a);

private:
	bool is_live;
};

void mThread::run(int a) {
	is_live = true;
	cout << a << endl;
	while (is_live) {
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		thread_callback();
	}
}

int main(){
	mThread demo;

	int n;
	cin >> n;
	return 0;
}

后记
在这个例子里面有个关键的问题,就是我们默认了

void mThread::run(int a) = void run(mThread *this,int a)

然后结果也在我们的意料之中,但是这样写不总是对的,因为有的编译器会将this指针放在参数列表的最后,而不是向我们一样放在最前面,也就是这样

void mThread::run(int a) = void run(int a,mThread *this)

这一点在amd和Intel之间表现得尤其明显,早期的amd处理器就是this放在最后,而intel一直都是this放在最前面。所以这样写其实在有的编译器上会通不过。
下面是另一种写法

#include <thread>
#include <chrono>

using namespace std;

class mThread;
void run(mThread* This,int a);
class mThread : public std::thread {
	friend void run(mThread* This, int a);
public:
	mThread() :std::thread(run,this,5) {
		this->detach();
	}
	virtual ~mThread() { is_live = false; }

	void thread_callback() {
		cout << "mThread run" << endl;
	}
private:
	bool is_live;
};

void run(mThread* This, int a) {
	cout << a << endl;
	This->is_live = true;
	while (This->is_live) {
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		This->thread_callback();
	}
}

int main(){
	mThread demo;

	int n;
	cin >> n;
	return 0;
}

这样手动This就可以避免上述问题。

<think> 首先,用户的问题是关于“std::chrono::milliseconds 与秒的关系及使用”,所以我需要基于提供的引用内容来回答。引用中包含了相关的信息,比如引用[1]、[5]等提到了std::chrono::milliseconds、seconds、以及转换方法。 从系统级指令开始: 1. 所有行内数学表达式必须使用$...$格式。例如,如果我提到一个公式或表达式,如时间单位转换,我应该用$...$包裹。 2. 独立公式必须使用$$...$$格式并单独成段。 3. 确保LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 在用户可见层: - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠,基于提供的引用。 关键点来自引用: - 引用[1]:列出了不同精度的时间单位,包括毫秒、秒等。 - 引用[5]:解释了std::chrono::duration,提到了ratio用于定义单位,如ratio<1>表示秒,ratio<1,1000>表示毫秒。还提到了使用duration_cast进行转换。 - 引用[3]:展示了计算时间差的例子,使用了milliseconds。 - 引用[4]:提到了获取时间的接口。 - 引用[2]:是一个问答系列,提到了seconds、milliseconds的定义和使用。 所以,回答应该包括: 1. std::chrono::milliseconds和std::chrono::seconds的定义:它们都是std::chrono::duration的别名,表示时间段。 - seconds是std::chrono::duration<Rep, std::ratio<1>>,即单位是秒。 - milliseconds是std::chrono::duration<Rep, std::ratio<1,1000>>,即单位是毫秒(1/1000秒)。 2. 关系:毫秒是秒的千分之一。1秒 = 1000毫秒。 3. 使用: - 创建对象:例如,std::chrono::seconds sec(10); // 10秒 - 转换:使用std::chrono::duration_cast进行转换。 - 示例代码:展示如何创建、转换和计算。 4. 添加引用标识:在相关部分末尾添加[^数字]。 结构清晰的回答: - 先介绍定义。 - 然后解释关系。 - 接着展示使用方法和示例。 - 最后生成相关问题。 确保使用中文。 生成相关问题: - 在回答的末尾,添加相关问题部分,如"§§相关问题§§",然后列出几个问题。 最终输出格式: - 回答主体。 - 相关问题。 现在,组织内容: 1. 定义: - std::chrono::seconds 表示以秒为单位的时间段[^2][^5]。 - std::chrono::milliseconds 表示以毫秒为单位的时间段,其中1毫秒 = 1/1000秒[^1][^5]。 2. 关系: - 1秒 = 1000毫秒。 - 在C++中,它们可以通过std::chrono::duration_cast进行转换。 3. 使用: - 创建:seconds sec(10); // 10秒 - 转换:milliseconds ms = duration_cast<milliseconds>(sec); // 10000毫秒 - 计算时间差:例如,使用system_clock获取时间点,然后计算差。 - 示例代码:基于引用[3]和[5],写一个简单的代码块。 4. 引用添加:自然地添加在信息后。 示例代码:确保是有效的C++代码。 例如: ```cpp #include <iostream> #include <chrono> using namespace std::chrono; int main() { // 创建10秒的时间段 seconds sec(10); // 转换为毫秒 milliseconds ms = duration_cast<milliseconds>(sec); std::cout << "10 seconds in milliseconds: " << ms.count() << " ms\n"; // 输出10000 // 获取当前时间点 auto start = high_resolution_clock::now(); // 做一些操作... auto end = high_resolution_clock::now(); // 计算时间差 auto diff = end - start; milliseconds diff_ms = duration_cast<milliseconds>(diff); std::cout << "Time taken: " << diff_ms.count() << " ms\n"; return 0; } ``` 在代码中,使用high_resolution_clock或system_clock,基于引用[4]。 生成相关问题: 1. 如何将毫秒转换为秒? 2. std::chrono::microseconds 和 nanoseconds 的使用方法? 3. 在实际编程中,如何测量代码执行时间? 确保LaTeX:如果提到数学关系,如1秒 = 1000毫秒,用行内数学表达式:$1 \text{秒} = 1000 \text{毫秒}$。 独立公式:可能不需要,但如果有复杂公式,用$$...$$。 现在,写回答。 </think> ### std::chrono::milliseconds 与秒的关系及使用 在C++标准库的`std::chrono`命名空间中,`std::chrono::milliseconds`和`std::chrono::seconds`都是时间段的表示,用于处理时间间隔。下面我将逐步解释它们的定义、关系及使用方法,确保回答基于可靠引用。 #### 1. **定义和基本关系** - **std::chrono::seconds**:表示以秒为单位的时间段。它是`std::chrono::duration`模板的别名,定义为`std::chrono::duration<Rep, std::ratio<1>>`,其中`std::ratio<1>`表示一个时钟周期为1秒[^2][^5]。 - **std::chrono::milliseconds**:表示以毫秒为单位的时间段。定义为`std::chrono::duration<Rep, std::ratio<1, 1000>>`,其中`std::ratio<1, 1000>`表示一个时钟周期为$1/1000$秒(即1毫秒)[^1][^5]。 - **核心关系**:毫秒(milliseconds)是秒(seconds)的千分之一,即: $$1 \text{秒} = 1000 \text{毫秒}$$ 这意味着转换时需注意单位换算,例如10秒等于10000毫秒。 #### 2. **使用方法** `std::chrono::milliseconds`和`std::chrono::seconds`主要用于创建、转换和计算时间间隔。以下是关键操作: - **创建时间段对象**: - 直接初始化:`seconds sec(10);` // 创建一个10秒的时间段[^5]。 - 或:`milliseconds ms(500);` // 创建一个500毫秒的时间段[^3]。 - **单位转换**: - 使用`std::chrono::duration_cast`进行转换: - **秒转毫秒**:将秒转换为毫秒时,乘以1000。 - **毫秒转秒**:将毫秒转换为秒时,除以1000(注意整数除法可能丢失精度)。 - 示例代码: ```cpp #include <iostream> #include <chrono> using namespace std::chrono; int main() { seconds sec(10); // 10秒 milliseconds ms = duration_cast<milliseconds>(sec); // 转换为毫秒 std::cout << "10 seconds = " << ms.count() << " ms\n"; // 输出:10000 ms milliseconds ms_val(2500); // 2500毫秒 seconds sec_val = duration_cast<seconds>(ms_val); // 转换为秒 std::cout << "2500 ms = " << sec_val.count() << " seconds\n"; // 输出:2 seconds(整数除法截断) return 0; } ``` 在这个例子中,`duration_cast`确保单位转换的正确性[^1][^5]。 - **计算时间差**: - 结合`std::chrono::system_clock`或`high_resolution_clock`测量代码执行时间[^4]。 - 示例:测量函数执行时间(单位:毫秒): ```cpp #include <iostream> #include <chrono> #include <thread> using namespace std::chrono; void some_function() { std::this_thread::sleep_for(milliseconds(200)); // 模拟耗时操作 } int main() { auto start = high_resolution_clock::now(); // 开始时间点 some_function(); // 执行函数 auto end = high_resolution_clock::now(); // 结束时间点 // 计算时间差并转换为毫秒 auto diff = end - start; milliseconds diff_ms = duration_cast<milliseconds>(diff); std::cout << "Time taken: " << diff_ms.count() << " ms\n"; // 输出约200 ms return 0; } ``` 此代码使用`high_resolution_clock`获取高精度时间点,并通过`duration_cast`将差值转换为毫秒[^3][^4]。 #### 3. **为什么使用这些类型?** - **封装优势**:`std::chrono::duration`模板封装了时间单位,避免了手动单位转换的错误(如混淆毫秒和微秒)。通过类型系统确保安全性,例如编译器会检查单位兼容性[^2][^5]。 - **精度控制**:直接使用`milliseconds`或`seconds`能使代码更易读,例如`sleep_for(milliseconds(500))`比裸数字更清晰[^4]。 - **性能**:基于模板的设计在编译时优化,无运行时开销。 ### 总结 `std::chrono::milliseconds`和`std::chrono::seconds`是C++时间处理的核心类型,关系为$1 \text{秒} = 1000 \text{毫秒}$。使用`duration_cast`进行转换,并常与时钟类(如`system_clock`)结合测量时间间隔。封装设计提高了代码的安全性和可读性[^1][^2][^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值