后台开发核心技术与应用实践学习笔记(九)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40028201/article/details/89552238

后台开发核心技术与应用实践学习笔记(九)

第9章 多线程

  1. 多进程频繁上下文切换会严重影响系统性能;进程间通信要求复杂的系统实现
  2. 同一进程内部的多个线程共享该进程的所有资源,通过线程可以支持同一个应用程序内部的并发,免去了进程频繁切换的开销;并发任务间的通信变得简单

9.1 多线程是什么

  1. 单线程一个进程只有一个控制权(函数调用时,该函数获得控制权),多线程允许有多个控制权
  2. 多线程的进程再内存中有多个栈,多个栈之间以一定的空白区域隔开以备栈的增长
  3. 由2可知多线程容易导致栈溢出(任何一个空白区域被填满就会导致栈溢出)

9.2 多线程的创建与结束

  1. 线程创建

    1. pthread_create创建成功,返回0;
    2. pthread_join用来等待一个线程的结束(还一种是函数已经结束,调用的线程也就结束了),一般主线程调用,等待子线程的退出,是阻塞的。
    3. pthread_exit一般子线程调用,用来结束当前线程。
    4. 子线程可以通过pthread_exit传递一个返回值,主线程可以通过pthread_join获得该返回值,从而判断该子线程的退出时正常还是异常
  2. 向线程传递参数

    pthread_create函数看起来只能传递一个参数给func,如果传递一个以上的参数,应该放在一个结构体

  3. 获得线程的id

    1. pthread_self函数获得线程id
    2. 创建函数时生成id,就是传进的参数thread

9.3 线程的属性

  1. 属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用,之后必须用pthread_attr_destroy函数来释放资源。
  2. 属性对象主要包括:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

9.4 多线程同步

​ 多线程相当于一个并发系统,一般执行多个任务。如果多个任务可以共享资源,特别是同时写入某个变量时,需要解决同步问题

  1. 多线程同步问题
    1. 并发情况下,线程执行顺序由内核决定,是不确定的。
    2. 多线程同步是指一定的时间内只允许某一个线程访问某个资源
    3. 方法:互斥锁、条件变量、读写锁、信号量
  2. 互斥锁
    1. 有两种状态,一般设置成全局变量,创建方式有两种,动态和静态
    2. 加锁将原先分离的多个指令构成不可分割的一个原子操作
  3. 条件变量
    1. 线程等待共享数据的某个条件出现,需要用到条件变量
    2. 允许线程阻塞和等待另一个线程发送信号的方法弥补互斥锁的不足。通常与锁一起使用
    3. 条件不满足,打开相应的互斥锁,一旦某个线程改变了条件变量,通知相应的条件变量唤醒被阻塞的线程,并将这些线程重新锁定并重新检测条件
  4. 读写锁
    1. 读时共享,写时独占
    2. 解决读者写者问题
    3. 两种常见的策略:强读者同步(读者优先权高,只要写者没操作即可访问资源)、强写者同步(等待所有正在的写者结束后才能执行),why?有啥区别
  5. 信号量
    1. 与互斥锁的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区
    2. 定义为全局变量,方便多个线程共享

9.5 多线程重入

​ 前面介绍了各种同步方式,其实是为了解决“函数不可重入”的问题,可重入函数指的是多任务并发使用,不用担心数据错误的函数,它可以任意被打断,稍后继续运行,且不会丢失数据。

  1. 可重入函数特点

    1.不在函数内部使用静态或者全局数据
    2.不返回静态或者全局数据,所有的数据都由函数调用者提供
    3.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
    4.如果必须访问全局数据,使用互斥锁来保护
    5.不调用不可重入函数

  2. 不可重入函数的特点

    1.函数中使用了静态变量,无论是全局静态变量还是局部静态变量
    2.函数返回静态变量
    3.函数中调用了不可重入函数
    4.函数中使用了静态的数据结构
    5.调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
    6.调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构

  3. _REENTRANT宏

    1.它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
    2.stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
    3.在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。

猜你喜欢

转载自blog.csdn.net/qq_40028201/article/details/89552238