POSIX 螺纹具体解释(1-概要)

2022-07-06 10:44:00 浏览数 (1)

大家好,又见面了,我是全栈君。

线程是有趣的

线程类似于进程。如同进程,线程由内核按时间分片进行管理。在单处理器系统中,内核使用时间分片来模拟线程的并发运行。这样的方式和进程的同样。

而在多处理器系统中,如同多个进程。线程实际上一样能够并发运行。

那么为什么对于大多数合作性任务。多线程比多个独立的进程更优越呢?这是由于,线程共享同样的内存空间。

不同的线程能够存取内存中的同一个变量。所以,程序中的全部线程都能够读或写声明过的全局变量。假设曾用 fork() 编写过重要代码。就会认识到这个工具的重要性。为什么呢?尽管 fork() 同意创建多个进程,但它还会带来下面通信问题: 怎样让多个进程相互通信。这里每一个进程都有各自独立的内存空间。对这个问题没有一个简单的答案。

尽管有很多不同种类的本地 IPC (进程间通信),但它们都遇到两个重要障碍:

  • 强加了某种形式的额外内核开销,从而减少性能。
  • 对于大多数情形。IPC 不是对于代码的“自然”扩展。通常极大地添加了程序的复杂性。

双重坏事: 开销和复杂性都非好事。

假设以前为了支持 IPC 而对程序大动干戈过,那么您就会真正赞赏线程提供的简单共享内存机制。因为全部的线程都驻留在同一内存空间。POSIX 线程无需进行开销大而复杂的长距离调用。

仅仅要利用简单的同步机制,程序中全部的线程都能够读取和改动已有的数据结构。而无需将数据经由文件描写叙述符转储或挤入紧窄的共享内存空间。

仅此一个原因,就足以让您考虑应该採用单进程/多线程模式而非多进程/单线程模式。

线程是快捷的

不仅如此。

线程相同还是非常快捷的。

与标准 fork() 相比。线程带来的开销非常小。内核无需单独复制进程的内存空间或文件描写叙述符等等。

这就节省了大量的 CPU 时间,使得线程创建比新进程创建快上十到一百倍。由于这一点,能够大量使用线程而无需太过于操心带来的 CPU 或内存不足。使用 fork() 时导致的大量 CPU 占用也不复存在。这表示仅仅要在程序中有意义,通常就能够创建线程。

当然,和进程一样。线程将利用多 CPU。假设软件是针对多处理器系统设计的。这就真的是一大特性(假设软件是开放源代码,则终于可能在不少平台上执行)。特定类型线程程序(尤其是 CPU 密集型程序)的性能将随系统中处理器的数目差点儿线性地提高。假设正在编写 CPU 很密集型的程序,则绝对想设法在代码中使用多线程。一旦掌握了线程编码,无需使用繁琐的 IPC 和其他复杂的通信机制。就行以全新和创造性的方法解决编码难题。全部这些特性配合在一起使得多线程编程更有趣、高速和灵活。

线程是可移植的

假设熟悉 Linux 编程,就有可能知道 __clone() 系统调用。

__clone() 类似于 fork()。同一时候也有很多线程的特性。比如,使用 __clone()。新的子进程能够有选择地共享父进程的运行环境(内存空间。文件描写叙述符等)。这是好的一面。

但 __clone() 也有不足之处。

线程的主要学习内容

  1. 线程管理
    1. 创建和终止线程
    2. 向线程传递參数
    3. 连接(Joining)和分离( Detaching)线程
    4. 栈管理
    5. 其他函数
  2. 相互排斥量(Mutex Variables)
    1. 相互排斥量概述
    2. 创建和销毁相互排斥量
    3. 锁定(Locking)和解锁(Unlocking)相互排斥量
  3. 条件变量(Condition Variable)
    1. 条件变量概述
    2. 创建和销毁条件变量
    3. 等待(Waiting)和发送信号(Signaling)

什么是线程

  • 技术上。线程能够定义为:能够被操作系统调度的独立的指令流。可是这是什么意思呢?
  • 对于软件开发人员,在主程序中执行的“函数过程”能够非常好的描写叙述线程的概念。
  • 进一步。想象下主程序(a.out)包括了很多函数,操作系统能够调度这些函数,使之同一时候或者(和)独立的执行。这就描写叙述了“多线程”程序。
  • 如何完毕的呢?
  • 在理解线程之前。应先对UNIX进程(process)有所了解。进程被操作系统创建,须要相当多的“额外开销”。进程包括了程序的资源和运行状态信息。例如以下:
    • 进程ID,进程group ID,用户ID和group ID
    • 环境
    • 工作文件夹
    • 程序指令
    • 寄存器
    • 文件描写叙述符
    • 信号动作(Signal actions)
    • 共享库
    • 进程间通信工具(如:消息队列。管道,信号量或共享内存)

UNIX PROCESS

THREADS WITHIN A UNIX PROCESS

  • 线程使用并存在于进程资源中,还能够被操作系统调用并独立地执行,这主要是由于线程只复制必要的资源以使自己得以存在并执行。
  • 独立的控制流得以实现是由于线程维持着自己的:
    • 堆栈指针
    • 寄存器
    • 调度属性(如:策略或优先级)
    • 待定的和堵塞的信号集合(Set of pending and blocked signals)
    • 线程专用数据(TSD:Thread Specific Data.)
  • 因此,在UNIX环境下线程:
    • 存在于进程,使用进程资源
    • 拥有自己独立的控制流。仅仅要父进程存在而且操作系统支持
    • 仅仅复制必能够使得独立调度的必要资源
    • 能够和其它线程独立(或非独立的)地共享进程资源
    • 当父进程结束时结束,或者相关类似的
    • 是“轻型的”。由于大部分额外开销已经在进程创建时完毕了
  • 由于在同一个进程中的线程共享资源:
    • 一个线程对系统资源(如关闭一个文件)的改变对全部其他线程是能够见的
    • 两个相同值的指针指向相同的数据
    • 读写同一个内存位置是可能的。因此须要成员显式地使用同步

使用线程设计程序

  • 在现代多CPU机器上,pthread很适于并行编程。能够用于并行程序设计的,也能够用于pthread程序设计。
  • 并行程序要考虑很多,例如以下:
    • 用什么并行程序设计模型?
    • 问题划分
    • 载入平衡(Load balancing)
    • 通信
    • 数据依赖
    • 同步和竞争条件
    • 内存问题
    • I/O问题
    • 程序复杂度
    • 程序猿的努力/花费/时间
  • 包括这些主题超出本教程的范围,有兴趣的读者能够高速浏览下“Introduction to Parallel Computing”教程。
  • 大体上,为了使用Pthreads的长处,必须将任务组织程离散的,独立的,能够并发运行的。比如,假设routine1和routine2能够互换。相互交叉和(或者)重叠,他们就能够线程化。
  • 拥有下述特性的程序能够使用pthreads:
    • 工作能够被多个任务同一时候运行。或者数据能够同一时候被多个任务操作。
    • 堵塞与潜在的长时间I/O等待。
    • 在某些地方使用非常多CPU循环而其它地方没有。
    • 对异步事件必须响应。
    • 一些工作比其它的重要(优先级中断)。
  • Pthreads 也能够用于串行程序,模拟并行执行。 非常好样例就是经典的web浏览器,对于多数人。执行于单CPU的桌面/膝上机器。很多东西能够同一时候“显示”出来。
  • 使用线程编程的几种常见模型:
    • 管理者/工作者(Manager/worker):一个单线程,作为管理器将工作分配给其他线程(工作者)。典型的,管理器处理全部输入和分配工作给其他任务。 至少两种形式的manager/worker模型比較经常使用:静态worker池和动态worker池。
    • 管道(Pipeline):任务能够被划分为一系列子操作,每个被串行处理。可是不同的线程并发处理。 汽车装配线能够非常好的描写叙述这个模型。
    • Peer: 和manager/worker模型相似,可是主线程在创建了其他线程后。自己也參与工作。

共享内存模型(Shared Memory Model):

  • 全部线程能够訪问全局。共享内存
  • 线程也有自己私有的数据
  • 程序猿负责对全局共享数据的同步存取(保护)

线程安全(Thread-safeness):

  • 线程安全:简短的说,指程序能够同一时候运行多个线程却不会“破坏“共享数据或者产生“竞争”条件的能力。
  • 比如:如果你的程序创建了几个线程,每个调用同样的库函数:
    • 这个库函数存取/改动了一个全局结构或内存中的位置。
    • 当每一个线程调用这个函数时,可能同一时候去改动这个全局结构活内存位置。
    • 假设函数没有使用同步机制去阻止数据破坏,这时。就不是线程安全的了。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/117215.html原文链接:https://javaforall.cn

0 人点赞