【C++并发编程】线程库的基本使用

为什么要使用多线程?

  • 为了提高程序执行的速度,可以将程序划分成多个相对独立的任务并发执行,实现并发可以采用多进程和多线程。多线程是指在一个进程中同时运行多个线程,线程共享进程的地址空间和系统资源。多线程方法中线程之间的切换开销较小,执行效率较高。
    注意:C++11才引入多线程支持。

进程与线程

  • 一个进程中的线程分为主线程和子线程。
    • 主线程是程序的起点,负责创建和管理其他线程。它通常执行程序的初始化操作,包括分配资源和设置环境,以及负责执行最后的关闭动作。
    • 子线程是由主线程创建的额外线程,它们可以并行执行,独立于主线程,通常用于执行耗时的任务,以避免阻塞主线程。
  • 一个进程只有一个主线程,要实现多线程,需要在主线程中额外创建子线程。

如何创建和管理子线程?

子线程执行普通函数

#include <iostream>
#include <thread>

int main()
{
	std::thread t([](){
		std::cout << "Hello World!" << std::endl;
		});
	t.join();

	return 0;
}
  • 使用std::thread创建线程
    std::thread t(func, args);
    
    • func是可调用对象,可以是函数名、Lambda函数等
    • args是可调用对象执行所需的参数
  • 使用join方法阻塞主线程,等待子线程执行完主线程才能继续执行。如果没有调用join方法,主线程可能在子线程执行完之前结束,提前释放地址空间和系统资源,导致子线程异常终止。
    注意:在调用join方法之前,最好先调用joinable判断线程是否可join。

子线程执行带有非常量左值引用参数的函数

#include <iostream>
#include <thread>
using namespace std;

static void ChangeValue(int& x, int val) {
	x = val;
}

int main() 
{
	thread th[100];
	int nums[100];

	for (int i = 0; i < 100; i++)
		th[i] = thread(ChangeValue, nums[i], i + 1);	// 编译报错
		// th[i] = thread(ChangeValue, std::ref(nums[i]), i + 1);	// 编译正常

	for (int i = 0; i < 100; i++) {
		th[i].join();
		cout << nums[i] << endl;
	}
	return 0;
}
  • 编译上述代码会报错,可以用std::ref对引用参数进行包装解决。主要原因是std::thread的构造函数会对参数进行移动操作,而可调用对象左值引用参数不接受右值引用,因此会报错。使用std::ref可以解决问题的原因是std::ref会生成一个reference_wrapper类型的临时对象,该对象重载了左值引用类型转换操作符,可以隐式转换为左值引用,而且由于是临时对象,移动操作相当于没有操作。

使用detach分离主线程和子线程

  • 使用detach方法后主线程不再等待子线程执行完,而是立即返回到主线程继续执行后续的代码,子线程结束时自行负责回收资源。
    注意:使用detach时,应确保子线程中的操作能够在主线程退出前完成,或者子线程中申请的资源能够正确释放
    #include <iostream>  
    #include <thread>  
    #include <chrono>  
    
    void threadFunction() {
        std::cout << "开始执行子线程" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(10)); // 模拟耗时操作  
        std::cout << "子线程结束" << std::endl;
    }
    
    int main() 
    {
        std::cout << "开始执行主线程" << std::endl;
        std::thread t(threadFunction);
    
        t.detach(); // 分离线程,让子线程在后台运行  
    
        std::cout << "继续执行主线程" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟主线程耗时操作
        std::cout << "主线程执行完毕" << std::endl;
    
        return 0;
    }
    
    • 执行上述代码,得到以下结果。没有子线程结束的提示
    开始执行主线程
    继续执行主线程
    开始执行子线程
    主线程执行完毕
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值