进程管理_多线程_C语言原生线程库(持续更新中)

111 阅读4分钟

基本概念

  • Linux中没有专门为线程设计TCB, 而是用进程的PCB来模拟线程, 所以线程也被称为轻量级进程. 这样的好处就是, ①不用维护复杂的线程与进程之间的关系, 不用单独为线程设计任何算法, 直接使用进程的一套相关方法; ②OS只需要聚焦在线程间的资源分配上就可以了.

  • 线程是系统调度的基本单位。即从调度PCB来看, 在CPU眼里, 它只会调度PCB, 至于这个PCB是线程还是进程, 它不关心.

  • 因为Linux

  • 多个同一个类型的线程, 共享一份地址空间、页表.

    image-20230505192549208

线程的优点、缺点、异常、用途

  • 优点

    • 创建一个新线程的成本要比创建一个新进程小得多。这里的成本是指“时间(CPU的时间) + 空间”(以后谈成本, 也是从这两个方向上谈)。
    • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
    • 线程占用的资源要比进程少很多
    • 能充分利用多处理器的可并行数量
    • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
    • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
    • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作
  • 缺点

    • 性能损失.

      一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

    • 健壮性降低

      编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

    • 缺乏访问控制

      进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

    • 编程难度提高

      编写与调试一个多线程程序比单线程程序困难得多

  • 异常

    • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃

    • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出

  • 用途

    • 合理的使用多线程,能提高CPU密集型程序的执行效率

    • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)

线程和进程的私有和共享的资源

  • 私有

    • 线程ID
    • 一组寄存器
    • errno
    • 信号屏蔽字
    • 调度优先级

    总结:每个线程都有自己的独自的栈和上下文。

  • 共享

    • 文件描述符表
    • 当前工作目录
    • 用户id和组id
    • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)

原生线程库

介绍

​ Linux因为是用进程模拟的,所以Linux下不会给我们提供直接操作线程的接口,而是给我提供在同一个地址空间创建PCB,分配资源给指定PCB的接口。因此有系统系别的工程师,在用户层对Linux轻量级进程接口进行封装,给我们打包成库(创建进程、释放进程......),让用户直接使用,这也叫原生线程库(所以,它也是第三方库)。

Linux中PID LWP TID之间的区别

  • PID

    是属于每个进程在系统中都有一个唯一·的非负整数表示的进程ID,用getpid() 获取进程ID。也就说同一类线程的PID是相等的。

  • LWP

    每个线程在系统中都有一个唯一的id标识符,用系统调用sys_call(SYS_gettid()) 获取自身线程pid。主线程LWP与所在进程pid相同。且CPU调根据LWP来调度不同的线程。

  • 线程tid

    线程tid(线程ID),每个线程在所属进程中都有一个唯一的线程ID,用pthread_self() 获取自身现成ID。有多个进程时,可能会出现多个线程ID相同的线程,故线程tid只在其所属的进程上下文中有意义,不能作为系统中某个线程的唯一标识符。

示例

  • 验证代码:

    #include <sys/types.h>
    #include <unistd.h>
    #include <iostream>
    #include <pthread.h>
     
    void *thread_run(void *args)
    {
      while(1)
      {
        sleep(1);
        const char *str = (const char*)args;
        printf("%s线程: %d\n", str, getpid());
        
      }
    }
    
    int main()
    {
      // create thread
      pthread_t tid;
      pthread_create(&tid, NULL, thread_run, (void*)"thread1");
    
      while(1)
      {
        std::cout << "main thread pid: " << getpid() << std::endl;
        sleep(1);
      }
    }
    
  • 执行结果:

    image-20230505205609629

    image-20230505205743299