Linux多线程

Linux线程概念

  • 线程是程序内部的执行路线,Linux中也称为轻量级进程。
  • 线程在进程地址空间内运行,共享大部分资源,如虚拟地址空间和页表。

二级页表:

  • 二级页表是虚拟内存管理的一种技术,用于将虚拟地址映射到物理内存地址。
  • 在32位平台下,使用页目录和页表的二级页表来实现地址映射。
  • 二级页表允许将大型地址空间划分成较小的单元,减少内存开销。

线程的优点:

  • 线程可以在同一进程内并发执行,提高多核处理器的利用率。
  • 线程之间共享内存,减少通信和同步的开销。
  • 轻量级,创建和销毁线程比进程更快。

线程的缺点:

  • 多线程编程复杂,容易引发竞态条件和死锁。
  • 线程之间共享内存可能导致数据一致性问题。
  • 线程调度和同步开销较大。

线程异常:

  • 线程可能面临异常,如分段错误(段错误),当尝试修改只读内存时会导致段错误。
  • 操作系统会终止触发异常的线程,通常通过信号来处理异常。

线程用途:

  • 线程用于多任务并发执行,如服务器、多媒体处理、并行计算等。
  • 线程可用于提高应用程序性能和响应性。

Linux进程VS线程:

  • 进程是程序的独立执行实体,拥有独立的地址空间和资源。
  • 线程是进程内的执行单元,共享进程的地址空间和资源。

Linux线程控制:

  • 原生线程库pthread提供线程管理接口。
  • 线程创建、等待、终止、分离等基本操作。
  • 线程ID是用于标识线程的唯一标识符。

线程的优点2:

  1. 创建一个新线程的代价比创建一个新进程小。
  2. 线程切换需要的操作系统工作较少,相比进程切换更轻量。
  3. 线程占用的资源较少,相比进程更节省。
  4. 可以充分利用多处理器的可并行数量,提高性能。
  5. 在等待慢速IO操作完成时,程序可以执行其他计算任务,提高效率。
  6. 对于计算密集型应用,可以将计算任务分解到多个线程中以在多处理器系统上运行。
  7. 对于IO密集型应用,可以重叠IO操作,线程可以同时等待不同的IO操作,提高性能。

线程的缺点2:

  1. 性能损失:计算密集型线程可能无法充分共享处理器,导致性能损失。
  2. 健壮性降低:多线程编程需要更全面的考虑,可能因时间分配或共享变量而导致问题。
  3. 缺乏访问控制:线程内部调用某些OS函数可能对整个进程产生影响。
  4. 编程难度增加:编写和调试多线程程序比单线程程序更复杂。

线程异常:

  • 单个线程出现异常(如除零或野指针),会导致线程崩溃,进而触发信号机制,终止进程。

线程用途:

  • 多线程可提高CPU密集型程序的执行效率。
  • 多线程可提高IO密集型程序的用户体验,例如同时进行下载和开发。

Linux进程VS线程:

  • 进程是承担分配系统资源的基本实体,线程是调度的基本单位。
  • 线程共享进程数据,但也有自己的一部分数据,如线程ID、寄存器、栈、errno等。
  • 进程内的多个线程共享代码段和数据段。
  • 主线程可以创建其他子线程。
  • Linux中,应用层的线程与内核的LWP(轻量级进程)一一对应,操作系统使用LWP进行调度。

线程控制

  • POSIX线程库(pthread)是Linux下的原生线程库。
  • 使用pthread_create函数创建新线程,传入线程ID、属性、线程例程函数和参数。
  • 主线程创建的新线程可以并发运行。

获取线程ID:

  1. 通过创建线程时的输出型参数获得线程ID。
  2. 通过调用pthread_self函数获得当前线程的ID,类似于获取进程ID的getpid函数。

线程等待:

  1. 线程需要被等待以释放其资源,否则可能导致内存泄漏。
  2. 使用pthread_join函数等待线程的结束。
  3. pthread_join函数的参数包括被等待线程的ID和线程退出时的退出码信息。
  4. 线程等待成功返回0,失败返回错误码。

线程终止:

  1. 线程可以通过以下方式终止:
    • 从线程函数中使用return语句退出,此时退出码可用于判断线程的正常运行结果。
    • 调用pthread_exit函数主动终止线程,同时指定退出码。
    • 一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。
  2. 线程使用return退出时,主线程退出会导致整个进程退出,资源被释放。
  3. 使用pthread_exit函数可以主动终止线程并设置退出码。

需要注意的细节:

  • pthread_join函数默认以阻塞方式等待线程结束。
  • 线程异常终止会导致整个进程崩溃,因此线程的健壮性较弱。
  • pthread_self函数获取的线程ID与内核的轻量级进程ID(LWP)不一定相等,它们有一对一的关系。
  • pthread_join只能获取到线程正常退出时的退出码,无法获取线程异常退出的信息。
  • 调用pthread_exitreturn退出线程时,指定的退出码所指向的内存单元必须是全局的或使用malloc分配的,而不是线程函数栈上的。

pthread_cancel 函数是用于取消线程的,以下是关于 pthread_cancel 函数的功能、用法、返回值、参数意义和需要注意的细节的总结:

功能: pthread_cancel 函数用于取消指定线程的执行。

用法:

int pthread_cancel(pthread_t thread);

  • thread:被取消线程的ID。

返回值:

  • 线程取消成功返回0,失败返回错误码。

参数意义:

  • thread:要取消的线程的ID。

需要注意的细节:

  1. 线程是可以取消的,包括可以自己取消自己。但通常情况下,线程控制是由主线程或其他线程来取消目标线程。
  2. 当线程被成功取消后,它的退出码通常会变成-1,而不是原来设定的退出码。
  3. 在示例代码中,通过 pthread_cancel(pthread_self()) 取消了线程的自身执行,这通常不是推荐的做法。
  4. 新线程也可以取消主线程,但这不是一种推荐的做法。通常情况下,我们使用主线程来控制其他新线程。
  5. 可以使用 pthread_detach 函数将线程分离,这样在线程退出后,系统会自动回收线程资源,无需显式调用 pthread_join
  6. pthread_t 类型的线程ID本质上是进程地址空间中共享区域的虚拟地址,用来唯一标识每个线程。不同的线程拥有不同的虚拟地址,这使得可以区分每个线程。
  7. 使用 pthread_self() 函数获取线程ID,它实际上返回一个虚拟地址,用于唯一标识当前线程。
  8. pthread_cancel 取决于线程库的实现,但在Linux的NPTL线程库中,线程ID本质上是共享区域上的虚拟地址。


函数总结

  1. pthread_create 函数

    • 原型:

      int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

    • 功能: pthread_create 用于创建新线程,并在新线程中执行指定的函数 start_routine
    • 参数意义:
      • thread:指向线程ID的指针,用于存储新线程的ID。
      • attr:线程属性,通常设置为 NULL,表示使用默认属性。
      • start_routine:线程执行的函数,这个函数接受一个 void* 参数,可以用来传递数据给新线程。
      • arg:传递给 start_routine 函数的参数。
    • 返回值:
      • 如果成功创建线程,返回0。
      • 如果出现错误,返回相应的错误码。
    • 注意事项:
      • 线程创建成功后,新线程会执行 start_routine 函数,可以通过 pthread_joinpthread_detach 来回收线程资源。
      • pthread_create 创建的线程默认是可连接(joinable)的,需要调用 pthread_join 来等待线程的结束并回收资源。
  2. pthread_cancel 函数

    • 原型:

      int pthread_cancel(pthread_t thread);

    • 功能: pthread_cancel 用于取消指定线程的执行。
    • 参数意义:
      • thread:被取消线程的ID。
    • 返回值:
      • 线程取消成功返回0,失败返回错误码。
    • 注意事项:
      • 线程可以被自己或其他线程取消,但通常建议由主线程或其他线程来取消目标线程。
      • 当线程被成功取消后,它的退出码通常会变成-1,而不是原来设定的退出码。
  3. pthread_detach 函数

    • 原型:

      int pthread_detach(pthread_t thread);

    • 功能: pthread_detach 用于将线程分离,使其在退出后自动回收资源,无需显式调用 pthread_join
    • 参数意义:
      • thread:要分离的线程的ID。
    • 返回值:
      • 线程分离成功返回0,失败返回错误码。
    • 注意事项:
      • 分离线程后,线程退出时系统会自动回收线程资源,无需再调用 pthread_join
      • 分离和可连接(joinable)是冲突的,同一线程不能同时被分离和设置为可连接。
  4. pthread_self 函数

    • 原型:

      pthread_t pthread_self(void);

    • 功能: pthread_self 用于获取当前线程的线程ID。
    • 参数意义:
      • 无参数。
    • 返回值:
      • 返回调用线程的线程ID,通常是一个虚拟地址,用于唯一标识当前线程。
    • 注意事项:
      • pthread_self 返回的线程ID用于标识当前线程,通常不需要显式创建,而是由线程库自动分配。
  5. pthread_join 函数

  • 原型:
  • int pthread_join(pthread_t thread, void **retval);
  • 功能: pthread_join 用于等待指定线程的结束,并获取其退出状态。
  • 参数意义:
  • thread:要等待的线程的ID。
  • retval:一个指向指针的指针,用于存储被等待线程的退出状态。
  • 返回值:
  • 如果成功等待线程的结束,返回0。
  • 如果出现错误,返回相应的错误码。
  • 注意事项:
  • pthread_join 会阻塞调用线程,直到指定线程结束为止。
  • 被等待线程的退出状态将被存储在 retval 指向的内存中,可以通过 **retval 来获取退出状态。
  • 如果不关心线程的退出状态,可以将 retval 设置为 NULL
  • 如果线程已经被分离,或者已经被另一个线程等待,再次调用 pthread_join 将导致错误。
  • 线程在退出时可以通过 pthread_exit 函数设置退出状态,退出状态通常是一个整数值,可以传递给其他线程以进行处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值