C++11 多线程编程 学习总结(上)

本文介绍了C++11的多线程编程基础知识,包括并发与线程的概念,如何开启和管理线程,以及线程间的参数传递。重点讨论了`detach()`和`join()`的区别,强调了`detach()`可能导致的问题和解决策略。此外,还探讨了线程安全的共享数据操作,如互斥量`mutex`的使用,以及如何预防死锁。
摘要由CSDN通过智能技术生成

基本概念

并发:一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

进程:可执行程序运行,便创建了一个进程。

线程

  • 就是代码的执行通路
  • 每个进程都有一个主线程,且唯一,与进程一起产生、结束
  • 多线程,不是越多越好,每个线程需要一个独立的堆栈空间,线程之间切换需切要保存很多中间的状态,切换过多降低程序的运行时间。

开启一个线程

整个进程是否执行完毕,取决于主线程是否执行完毕,即如果主线程执行完毕整个进程也结束,此时如果子线程没有结束,也会被强行结束。因此如果需要让子线程一保持正常运行,主线程一般会保持一致运行,但是也有例外(即detach)。

开启线程

1.需要用到的头文件: #include <thread>since supported by c++11;

2.线程,就是开辟一个新的通道去执行另一个函数,对象如下:

    1)普通函数

    2)仿函数(functor):类里面实现operator()

    3)Lambda表达式

3.使用的函数:

    1)join()

    2)detach()

    区别

    join()函数,让主线程等待子线程执行完成,相当于阻塞,join执行完毕,join()后面的主线程才会继续执行。这样可以有效防止主程序已经停止运行,而子程序没有运行结束,子程序不得不中断。

    detach()函数,让子线程和主线程分开独自执行,无法控制,该子线程由c++运行时库进行控制管理,驻留后台运行,运行结束也是由c++运行时库进行释放相关资源(守护线程)。但是当主线程结束,该子线程也会随即中断结束。

    注意事项

    1)在使用detach函数时,注意子线程和主线程之间不要使用“引用”方式来传递参数,否则某一个先结束,释放变量,会导致另一个线程出现bug。

    2)join()函数一般置于return语句前面,等待所有的子线程运行结束,主线程再结束。

 4.简单案例

#include <thread>
#include <iostream>
		
void foo() {
    std::cout << "subthread" << std::endl;
}
		
int main(int argc, char const *argv[]){
    std::thread thread(foo);    // 启动线程foo,并且已经开始执行
	thread.join();              // 让主线程等待子线程执行完成,相当于阻塞,join执行完毕,主线程再继续执行
	std::cout<<"I am main thread.\n";
	return 0;
}

    上面关键的两步:

        std::thread thread(foo);   

        thread.join();             

    首先要创建线程的实例,然后进行阻塞。由于Lambda表达式也可以创建函数,因此也可以用Lambda表达式代替foo,同理functor。

线程传参

1.detach引发的问题

    由于detach使得主线程和该子线程进行了分离,主线程不再等待该子线程完成就结束,就可能会导致如下问题:

    如果从主线程以引用的方式传参数给子线程,当主线程运行结束,该参数也被释放,就会导致子线程在接受参数时接收到的是一个被释放的内存区域,引发不可预料的问题。

    解决办法:

a)若传递int这种简单类型参数,建议都是值传递,不要用引用。防止节外生枝。

b)如果传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里用引用来接;否则系统还会构造一次对象

终极结论:

c)建议不使用detach(),只使用join():这样就不存在局部变量失效导致线程对内存的非法引用问题。

2.获取当前线程id

std::this_thread::get_id()

3.传递类对象的引用到线程函数,std::ref()函数

4.线程函数参数

  • 普通函数
  • 仿函数(functor)
  • 智能指针作为参数
  • 类的成员函数指针作为线程参数

(1)functor需要实现operator() ,调用如下,可见只是需要传入一个类的实例即可,自动的调用functor.

输入:
class baz{
public:
	void operator()(int n) {
	    std::cout<<"var address in sub: "<<&n<<"|var value in sub: "<<n<<std::endl;
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
	int n = 0;
};
			
int main(int argc, char const *argv[]){
    baz b;
    int n=10;
    std::thread t6(b,n); // t6 runs baz::operator() on object b 
			       
    std::cout<<"var address in main: "<<&n<<"|var value in main: "<<n<<std::endl;
    t6.join();
    return 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值