C++多线程(一)----创建线程、常用的线程处理函数、处理线程的返回值 (详细解析~~~)

本文介绍了C++11开始提供的内置多线程库,包括如何通过普通函数、lambda表达式、带参数创建线程,以及线程操作如join、detach、get_id、sleep_for和yield。还讨论了带返回值的线程处理函数,使用async、packaged_task和promise获取返回值的方法。
摘要由CSDN通过智能技术生成

1. C++多线程库

传统的C++(C++11之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h>

C++11提供了语言层面上的多线程,包含在头文件中。它解决了跨平台的问题,提供了管理线程、保护共享数据、线程间同步操作、原子操作等类。C++11 新标准中引入了5个头文件来支持多线程编程,如下图所示:
图来自https://blog.csdn.net/QLeelq/article/details/115747717

2、如何使用C++线程库创建多线程

2.1、普通函数充当线程处理函数创建线程

// 1、普通函数充当线程处理函数创建线程
void print() {
	cout << "线程启动函数" << endl;
}
void test1() {
	thread t1(print);    //创建线程
	if (t1.joinable()) {   //判断是否可以被加入
		t1.join();
	}
}

/*
并发实现
    1、多个进程实现并发
        1.1 主要解决的是进程间通信问题
            1.1.1 同一台电脑上,主要通过管道、文件、消息队列、内存共享实现
            1.1.2 不同电脑主要网络编程实现
        
    2、单进程多线程实现并发
        2.1 一个进程中所有线程共享内存内存空间的形式
        2.2 资源竞争

C++中的多线程
    #include<thread>
    创建线程,线程处理函数
    创建线程未做任何处理,调用abort终止我们的程序
    join();加入\汇合线程,阻塞主线程
    detach();分离,驻留后台
    对于同一线程,join(),detach()只能调用一次
    joinable() 判断当前线程是否可以被join或detach,如果可以返回true,否则返回false

*/
2.2、采用Lambda表达式充当线程处理函数
// 2、采用Lambda表达式充当线程处理函数
void test2() {
	thread t1([]() {cout << "Lambda式充当线程处理函数" << endl; });   //创建线程
	if (t1.joinable()) {
		t1.join();
	}
}
2.3、带参数创建线程
  • 1、普通参数
// 普通参数
void printData(int num) {
	cout << "id:" << num << endl;
}
void test3() {
	int num = 1;
	thread t1(printData, num);    //拷贝
	if (t1.joinable()) {
		t1.join();
	}
}
  • 2、传引用参数
	//传引用
void printReference(int& num) {
	num = 1001;
	cout << "子线程" << num << endl;
}
void test4() {
	int num = 1;
	thread t1(printReference, ref(num));      //ref:包装引用作为传递的值
	if (t1.joinable()) {
		t1.join();
	}
	cout << "主线程" << num << endl;
}
  • 3、传智能指针
	//传智能指针
void printPtr(unique_ptr<int> ptr) {
	cout << "智能指针的值:" << *ptr.get() << endl;  //100
}
void test5() {
	unique_ptr<int> ptr(new int(100));
	thread t1(printPtr,move(ptr));  //move:对象转移
	if (t1.joinable()) {
		t1.join();
	}
	cout << "主线程" << ptr.get() <<endl;  //此处为0,因为ptr对象已经转移
}
2.4、带参数创建线程
  • 1、仿函数形式:类名的方式调用
// 仿函数形式:类名的方式调用
class Function {
public:
	void operator()() {
		cout << "重载()" << endl;
	}
};
void test6() {
	//对象
	Function object;
	thread t1(object);
	if (t1.joinable()) {
		t1.join();
	}
	//匿名对象
	thread t2((Function()));       //此处需要注意括号的数量
		if (t2.joinable()) {
		t2.join();
	}
}
  • 2、普通类中的成员函数
//普通类中的成员函数
class MM {
public:
	void print(int& id) {
		cout << "id:" << id << endl;
	}
};

void test7() {
	MM mm;
	int id = 1;
	thread t1(&MM::print, mm, ref(id));   //第一个是类的成员函数指针,第二个参数指明函数指针属于那个对象,第三个是参数
	if (t1.joinable()) {
		t1.join();
	}
}

创建线程的完整的测试代码

#include<iostream>
#include<thread>

using namespace std;

// 1、普通函数充当线程处理函数创建线程
void print() {
	cout << "线程启动函数" << endl;
}
void test1() {
	thread t1(print);
	if (t1.joinable()) {
		t1.join();
	}
}

// 2、采用Lambda表达式充当线程处理函数
void test2() {
	thread t1([]() {cout << "Lambda式充当线程处理函数" << endl; });   //创建线程
	if (t1.joinable()) {
		t1.join();
	}
}

// 3、带参数创建线程
	// 3.1 普通参数
void printData(int num) {
	cout << "id:" << num << endl;
}
void test3() {
	int num = 1;
	thread t1(printData, num);
	if (t1.joinable()) {
		t1.join();
	}
}
	//3.2传引用
void printReference(int& num) {
	num = 1001;
	cout << "子线程" << num << endl;
}
void test4() {
	int num = 1;
	thread t1(printReference, ref(num));      //ref:包装引用作为传递的值
	if (t1.joinable()) {
		t1.join();
	}
	cout << "主线程" << num << endl;
}

	//3.3传智能指针
void printPtr(unique_ptr<int> ptr) {
	cout << "智能指针的值:" << *ptr.get() << endl;  //100
}
void test5() {
	unique_ptr<int> ptr(new int(100));
	thread t1(printPtr,move(ptr));
	if (t1.joinable()) {
		t1.join();
	}
	cout << "主线程" << ptr.get() <<endl;  //此处为0,因为ptr对象已经转移
}

// 4、通过类中成员函数去创建
// 4.1仿函数形式:类名的方式调用
class Function {
public:
	void operator()() {
		cout << "重载()" << endl;
	}
};
void test6() {
	//对象
	Function object;
	thread t1(object);
	if (t1.joinable()) {
		t1.join();
	}
	//匿名对象
	thread t2((Function()));       //此处需要注意括号的数量
		if (t2.joinable()) {
		t2.join();
	}
}

// 4.2普通类中的成员函数
class MM {
public:
	void print(int& id) {
		cout << "id:" << id << endl;
	}
};

void test7() {
	MM mm;
	int id = 1;
	thread t1(&MM::print, mm, ref(id));   //第一个是类的成员函数指针,第二个参数指明函数指针属于那个对象,第三个是参数
	if (t1.joinable()) {
		t1.join();
	}
}


int main() {

	test1();
	test2();
	test3();
	test4();
	test5();
	test6();
	test7();
	return 0;
}

3、4个对线程操作的函数

1、get_id() 获取线程id
//No.1 获取线程id
void threadFunc() 
{
	cout << "id:" << this_thread::get_id() << endl;
	using namespace this_thread;
	cout << "id:" << get_id() << endl;
}
2、sleep_for() 延时函数
//No.2 sleep_for 延时函数
void threadSleep_for() 
{
	cout << "Sleep_for_id:" << this_thread::get_id() << "启动" << endl;
	// this_thread::sleep_for(2s);
	this_thread::sleep_for(chrono::seconds(3));    //让线程延迟3秒执行
	cout << "Sleep_for_id:" << this_thread::get_id() << "结束" << endl;
}
3、yield() 让线程放弃执行
//No.3 yield 让线程放弃执行 ,让操作系统调用另一个线程执行
void threadYield(chrono::microseconds duration)
{
	cout << "Yield id:" << this_thread::get_id() << "启动" << endl;
	auto startTime = chrono::high_resolution_clock::now();
	auto endTime = startTime + duration;
	do 
	{
		this_thread::yield();
		cout << 1 << endl;
	} while (chrono::high_resolution_clock::now() < endTime);
	cout << "Yield id:" << this_thread::get_id() << "结束" << endl;
}
  • 在C++11标准中,std::this_thread::yield()函数可以用来提示调度器当前线程愿意让出CPU资源,使得其他线程或进程可以被调度执行。具体来说,yield()函数会使得当前线程从运行状态变为就绪状态,让出CPU,以便其他线程可以继续执行。
  • 然而,需要注意的是,yield()函数并不会导致当前线程被完全阻塞,即它并不能保证当前线程会被暂停一段时间,也不能保证其他线程会立即被调度执行。调度器可以选择忽略yield()的提示,而继续执行当前线程,或者切换到其他线程。
  • 因此,yield()函数的主要作用是提示调度器当前线程可以被调度器抢占,以尽量减小系统的负载,避免忙等待和饥饿现象,而不是堵塞当前线程。如果需要在特定的时间段内堵塞当前线程,可以使用其他函数,
    例如std::this_thread::sleep_for()std::this_thread::sleep_until()函数。
4、sleep_until() 阻塞线程,直到sleep_time溢出
//No.4 阻塞线程,直到sleep_time溢出
void threadSleep_until() {
	time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now());    // 返回当前的时间点,使用的是系统时钟
	cout << "999:" << t << endl;
	tm* ptm = new tm;      //创建一个tm结构体指针ptm,用于存储时间的分解信息
	localtime_s(ptm, &t);   //使用localtime_s()函数将时间戳转换为本地时间,并将结果保存在ptm指向的结构体中。
	if (ptm != nullptr) {   //确保时间转换成功。
		this_thread::sleep_until(chrono::system_clock::from_time_t(mktime(ptm)));  // 使用chrono::system_clock::from_time_t()函数将ptm中的时间信息转换为chrono::system_clock的时间点类型
	}
	cout << put_time(ptm, "%X");  //最后,使用put_time()函数将ptm中的时间信息以指定的格式输出。
}

完整的测试代码

#include<iostream>
#include<chrono>
#include<thread>
#include<iomanip>

using namespace std;
//No.1 获取线程id
void threadFunc()
{
	cout << "子线程id:" << this_thread::get_id() << endl;
	using namespace this_thread;    //使用命名空间           get_id()属于this_thread命名空间下的
	cout << "子线程id:" << get_id() << endl;
}

// No.2 sleep_for() 延时函数
void threadSleep_for() {
	cout << "Sleep_for id:" << this_thread::get_id()<<"启动成功"<<endl;
//	this_thread::sleep_for(2s);    //延时2s执行
	this_thread::sleep_for(chrono::seconds(2));   //延时2s执行,和上面一样的效果
	cout << "Sleep_for id:" << this_thread::get_id() << "线程结束" << endl;
}

//No.3 yield() 让线程放弃执行,让操作系统调度另一个线程执行
void threadYield(chrono::microseconds duration) {
	cout << "Yield id:" << this_thread::get_id() << "启动" << endl;
	auto startTime = chrono::high_resolution_clock::now();
	auto endTime = startTime + duration;
	do {
		this_thread::yield();
		cout << 1 << endl;
	} while (chrono::high_resolution_clock::now() < endTime);
	cout << "Yield id:" << this_thread::get_id() << "结束" << endl;
}


//No.4 阻塞线程,直到sleep_time溢出
void threadSleep_until() {
	time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now());    // 返回当前的时间点,使用的是系统时钟
	cout << "999:" << t << endl;
	tm* ptm = new tm;      //创建一个tm结构体指针ptm,用于存储时间的分解信息
	localtime_s(ptm, &t);   //使用localtime_s()函数将时间戳转换为本地时间,并将结果保存在ptm指向的结构体中。
	if (ptm != nullptr) {   //确保时间转换成功。
		this_thread::sleep_until(chrono::system_clock::from_time_t(mktime(ptm)));  // 使用chrono::system_clock::from_time_t()函数将ptm中的时间信息转换为chrono::system_clock的时间点类型
	}
	cout << put_time(ptm, "%X");  //最后,使用put_time()函数将ptm中的时间信息以指定的格式输出。
}

int main() {
	cout << "主线程id:" << this_thread::get_id() << endl;
	thread t3(threadYield, chrono::microseconds(100));
	t3.detach();

	thread t1(threadFunc);
	t1.join();

	thread t2(threadSleep_for);
	t2.join();
	threadSleep_until();


	cout << 1111 << endl;
	return 0;
}

4、带返回值的线程处理函数

1、带返回值的普通函数充当线程处理函数
//No.1 带返回值的普通函数充当线程处理函数
int testThreadOne()
{
	return 1001;
}
2、带返回值的类的成员函数充当线程处理函数
//No.1 带返回值的普通函数充当线程处理函数
//No.2 带返回值的类的成员函数充当线程处理函数
class MM
{
public:
	int mmThread(int num)
	{
		cout << "MMthread_id:" << this_thread::get_id() << endl;
		num *= 2;
		chrono::milliseconds duration(5000);  
		this_thread::sleep_for(duration);  //使当前线程休眠 5 秒,即暂停执行
		return num;
	}
};

void testAsync() {
	future<int> result = async(testThreadOne);
	cout << result.get() << endl;

	MM mm;
	auto temp = async(&MM::mmThread, &mm, 1999);   //async(&MM::mmThread, mm, 1999) 在使用类成员函数作为异步任务时,应该将类实例mm作为引用或指针传递给async函数。
	cout << temp.get() << endl;

	//async的其他参数
	//launch::async			:  创建线程,执行线程处理函数(默认是这种方式)
	//launch::deferred		:  线程处理函数延迟到 我们调用wait()和get()方法时候才会执行,本质是没有创建子线程↓

	auto testDerred = async(launch::deferred, &MM::mmThread, &mm, 1998);
	cout<<testDerred.get();     //只有调用get(),才会创建线程

	auto testAsyncParam = async(launch::async, &MM::mmThread, &mm, 100);  //直接创建线程
}
3、用thread处理带返回值的线程处理函数
//No.3 用thread处理带返回值的线程处理函数
//1.通过类模板  packaged_task 包装线程处理函数
//2.通过packaged_task的对象调用get_future获取future对象,再通过get()方法得到子线程处理函数的返回值
void testPackage_task() {
	//No.1打包普通函数
	packaged_task<int(void)> taskOne(testThreadOne);    //int(void)  int:函数返回值类型,(void):函数参数类型
	thread  testOne(ref(taskOne));
	testOne.join();
	cout << taskOne.get_future().get() << endl;
	//No.2 打包类的成员函数
	MM mm;
	packaged_task<int(int)> taskTwo(bind(&MM::mmThread, &mm, placeholders::_1));   //注意这里使用占位符
	thread testTwo(ref(taskTwo),20);
	testTwo.join();
	cout << taskTwo.get_future().get() << endl;

	//No.3 打包Lambda表达式
	packaged_task<int(int)> taskThree([](int num)
		{
			cout << "thread_id:" << this_thread::get_id() << endl;
			num *= 10;
			return num;
		});
	thread testThree(ref(taskThree), 7);
	testThree.join();
	cout << taskThree.get_future().get() << endl;
}
4、 promise 获取线程处理函数"返回值"
void testPromiseThread(promise<int>& temp, int data)
{
	cout << "thread_id" << this_thread::get_id() << endl;
	data *= 3;
	temp.set_value(data);
}
void testPromise()
{
	promise<int> temp;
	thread testp(testPromiseThread, ref(temp), 18);
	testp.join();
	cout << temp.get_future().get() << endl;    
}

完整的测试代码

#include<iostream>
#include<thread>
#include<chrono>
#include<future>

using namespace std;


int returnValue() {
	return 100;
}

/*
	第一步:采用async:启动一个异步任务,(创建线程,并且执行线程处理函数),返回值future对象
	第二步:通过future对象中get()方法获取线程函数返回值
*/

//No.1 带返回值的普通函数充当线程处理函数
int testThreadOne()
{
	return 1001;
}


//No.2 带返回值的类的成员函数充当线程处理函数
class MM
{
public:
	int mmThread(int num)
	{
		cout << "MMthread_id:" << this_thread::get_id() << endl;
		num *= 2;
		chrono::milliseconds duration(5000);  
		this_thread::sleep_for(duration);  //使当前线程休眠 5 秒,即暂停执行
		return num;
	}
};

void testAsync() {
	future<int> result = async(testThreadOne);
	cout << result.get() << endl;

	MM mm;
	auto temp = async(&MM::mmThread, &mm, 1999);   //async(&MM::mmThread, mm, 1999) 在使用类成员函数作为异步任务时,应该将类实例mm作为引用或指针传递给async函数。
	cout << temp.get() << endl;

	//async的其他参数
	//launch::async			:  创建线程,执行线程处理函数(默认是这种方式)
	//launch::deferred		:  线程处理函数延迟到 我们调用wait()和get()方法时候才会执行,本质是没有创建子线程↓

	auto testDerred = async(launch::deferred, &MM::mmThread, &mm, 1998);
	cout<<testDerred.get();     //只有调用get(),才会创建线程

	auto testAsyncParam = async(launch::async, &MM::mmThread, &mm, 100);  //直接创建线程
}

//No.3 用thread处理带返回值的线程处理函数
//1.通过类模板  packaged_task 包装线程处理函数
//2.通过packaged_task的对象调用get_future获取future对象,再通过get()方法得到子线程处理函数的返回值
void testPackage_task() {
	//No.1打包普通函数
	packaged_task<int(void)> taskOne(testThreadOne);    //int(void)  int:函数返回值类型,(void):函数参数类型
	thread  testOne(ref(taskOne));
	testOne.join();
	cout << taskOne.get_future().get() << endl;
	//No.2 打包类的成员函数
	MM mm;
	packaged_task<int(int)> taskTwo(bind(&MM::mmThread, &mm, placeholders::_1));   //注意这里使用占位符
	thread testTwo(ref(taskTwo),20);
	testTwo.join();
	cout << taskTwo.get_future().get() << endl;

	//No.3 打包Lambda表达式
	packaged_task<int(int)> taskThree([](int num)
		{
			cout << "thread_id:" << this_thread::get_id() << endl;
			num *= 10;
			return num;
		});
	thread testThree(ref(taskThree), 7);
	testThree.join();
	cout << taskThree.get_future().get() << endl;
}

//No.4 promise 获取线程处理函数"返回值" 
//第一步:通过promise类模板构建对象,通过调用set_value 是存储函数需要返回的值
//第二步:通过get_future获取future对象,再通过get()方法获取线程处理函数中值

void testPromiseThread(promise<int>& temp, int data)
{
	cout << "thread_id" << this_thread::get_id() << endl;
	data *= 3;
	temp.set_value(data);
}
void testPromiseToThread(future<int>& temp)
{
	int num = temp.get();
	cout << num << endl;
}
void testPromise()
{
	promise<int> temp;
	thread testp(testPromiseThread, ref(temp), 18);
	testp.join();
	cout << temp.get_future().get() << endl;    
	//temp.set_value(888);    
	promise<int> data;
	data.set_value(999);
	auto num = data.get_future();
	thread test(testPromiseToThread, ref(num));
	test.join();
}

int main() {
//	thread t1(returnValue);   //这样创建线程无法获取线程的返回值
//	t1.join();    
//	future<int> result = async(testThreadOne);
//	cout << result.get() << endl;  
//	testAsync();
//	testPackage_task();
	testPromise();
	return 0;
}


Time:2023.5.19 (周五)
如果上面代码对您有帮助,欢迎点个赞!!!

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值