多线程编程

《C++ 11 多线程编程》

1. 基础

  进程:一个进程代表计算机中实际运行的一个程序,在现代计算机操作系统的保护模式下,每个进程都具有自己独立的进程地址空间和上下文堆栈;进程并不负责执行进程代码,只是为程序提供一个大环境容器,进程中的实际执行体是线程(Thread),因此在一个进程中至少得有一个线程,这个线程被称为主线程;

  线程是进程中实际执行代码的最小单元,具体由操作系统安排调度,决定其合适启动,运行,暂停,消亡;

  在Windows系统下,当一个进程存在多个子线程的时候,如果主线程执行结束,这时子线程(支线程)即使还没有执行完相应的代码,也会退出。因此在Windows下设计多线程程序的时候,需要确保在子线程执行完之前,主线程保持不退出;

  Linux系统下,主线程退出,不会影响子线程的执行,子线程还会继续运行,但是此时的进程会变成僵尸进程。在Linux下设计多线程程序时,应该避免这种情况;

某个线程奔溃,是否会导致进程退出?

一般来说,每个线程都是一个独立的执行单位,有自己的上下文堆栈,一个线程的奔溃不会对其他的线程造成影响;但是实际情况是,一个线程奔溃,会导致整个进程退出;例如Linux下会产生一个Segment Fault (段错误)信号,操作系统对这个信号的默认处理方式就是结束进程;整个进程都被销毁,则进程中其他线程自然也不会存在了;

2. 等待线程结束

  Linux下通过调用pthread_join()函数等待线程结束,pthread_join()函数在等待目标线程退出期间,会挂起当前线程,被挂起的线程出于等待状态,不会消耗CPU时间片;直到目标线程退出之后,调用pthread_join()的线程才会被唤醒,继续执行后续的逻辑。

  C++11等待线程结束:c++11的线程std::thread提供了join()方法来等待线程结束,使用这个方法时,必须保证线程是处于运行状态,即等待的线程必须是可join的,如果需要等待的线程已经退出,此时调用join方法,会导致程序崩溃;因此c++11线程库还提供了一个joinable()方法,判断一个线程是否是可以join()的。

3. c++ 11多线程

关于c++11多线程有以前的两篇博客可参考:

C++多线程编程 (1)
C++多线程编程 (2)

c++11之前,并没有对多线程提供语言级别的支持;这使得用c++编写可移植的并发程序时,存在诸多不便;

c++多线程相关知识点:

  • 线程同步的互斥量
  • 线程通信的条件变量
  • 线程安全的原子变量
  • call once用法
  • 用于异步操作的future, promise, task
  • 线程异步操作函数async

4. 整型变量的原子操作

  线程同步技术,指的是多个线程同时操作某个资源,这里的资源可能是简单的整型变量,或者是一个复杂的对象;对资源的操作一般是指对资源的读写操作,在操作过程中需要采取一些措施对这些资源进行保护,避免引起资源访问冲突,或者得到意料之外的结果。

4.1 整型变量是不是原子操作

  整型变量操作大致有以下三种情况:

  • 给整型变量赋值
int a = 110;

这样的操作一般是原子操作,需要一条计算机指令就能完成,即CPU将立即数110搬运到变量a的内存地址中;

  • 变量自增或者自减
a++;
a--;

从C++层面,这条语句是一个原子操作,但是从编译器得到的汇编指令来看其实不是,这条语句对应三条指令;1. 首先将变量a对应的值mov到某个寄存器。2. 将寄存器中的值自增1。 3. 将寄存器中自增后的值mov到变量a对应的内存地址;

例如:两个线程同时对变量a进行操作

int a = 0;

// 线程1 操作a自增
thread1()
{
    a++;
}

// 线程2 操作a自增
thread2()
{
    a++;
}

假设线程1将变量a的值加载到寄存器,对其进行自增操作,此时还未将寄存器中的值mov回变量地址内;由于操作系统调度的不确定性,切换到线程2开始执行,此时将a变量的值(还是0)mov到寄存器中,进行自增操作,寄存器内的值也为1;此时切换线程1继续执行后,a变量的值被置为1,线程2结束后,a变量的值也为1,与预想的值(2)不相等。

  • 把一个变量的值赋给另一个变量,或者是把一个表达式的值赋给另一个表达式

例如:

int a = b;

  从汇编的角度来看,这条语句也需要多条指令才能完成,由于CPU架构体系的限制,数据不能从内存的一处直接搬运的另一处,必须通过寄存器中转才能完成;此时如果是多线程操作,则会产生不确定性的结果;

2. C++11 对整型变量原子操作的支持
posted @ 2023-10-06 18:20  Alpha205  阅读(33)  评论(0编辑  收藏  举报