一般而言,哪些线程共享?


20

好吧,这是一个普遍的问题。而且,如果有人想使其实现特定,那么我将更喜欢Unix相关的东西。但首先需要普遍了解以下问题:

我读到单个进程可以有多个线程。同一进程的多个线程确实在它们之间共享事物。我想知道他们分享什么,不分享什么。考虑到进程由地址空间,堆栈,堆,全局变量,代码,数据,OS资源组成,线程之间共享哪些资源?我有以下猜测:

  1. 全局变量-我已读取线程共享全局变量。同样,在用Java和C#编程时,我创建了线程来共享类级变量。因此,我认为线程共享全局变量(尽管不能确定高级编程语言中的概念是否可以转换为低操作系统级别的事实)。

  2. 堆-由于全局变量存储在堆中,因此堆在线程之间共享。

  3. 堆栈-由于每个线程可以具有自己的执行序列/代码,因此它必须具有自己的堆栈,可以在该堆栈上推入/弹出程序计数器的内容(在发生函数调用和返回的情况下)。因此,同一进程的线程不共享堆栈。

现在我不确定以下内容的共享

  1. 地址空间-不确定在地址空间中到底要算什么。但是我想地址空间通常用于进程而不是线程的上下文中。而且,由于同一进程的所有线程与父进程位于同一地址空间,因此可以说线程共享地址空间。(但是,然后它们在同一地址空间内维护不同的堆栈吗?)

  2. 操作系统资源-我猜这可能是特定于实现的。例如,父进程可以选择性地将同一文件的句柄分配给它的某些线程,而不是全部。还是我误会了,操作系统资源意味着文件以外的东西?

  3. 代码-线程可以具有不同的代码,因此共享代码并非总是如此。

  4. 数据-不确定要在数据下考虑什么。但是请确保全局变量在线程之间共享。并确保不会局部共享局部变量。

总体而言,由于术语含糊,操作系统书籍中的超级概括以及在线提供的额外的实现特定细节,我感到非常困惑。因此,我试图找到一些令我满意的答案。

Answers:


13

通常,每个线程都有自己的寄存器(包括自己的程序计数器),自己的堆栈指针和自己的堆栈。其他所有内容在共享进程的线程之间共享。

特别是,通常认为一个进程由一组共享地址空间,堆,静态数据和代码段以及文件描述符*的线程组成。

一个地址空间是简单的逻辑地址的物理内存的某些片段的映射。因此,当我们说一个进程中的所有线程共享相同的地址空间时,是指foo在全局范围内访问变量时,所有线程将看到相同的变量。同样,线程可能在任何特定时间都在代码中运行不同的点,但是都允许它们调用全局函数bar(),该函数对于进程中的每个线程都对应于同一函数。

大多数现代操作系统都增加了线程本地存储的概念,它们是不共享的全局范围变量。使用此方法的常见示例是变量errno。那是一个全局范围的变量,但是在大多数现代操作系统中,每个线程都有自己的本地副本,因此一个线程上的库调用中的错误不会影响其他线程的行为。

*进程中的所有线程共享一些其他进程状态,例如进程ID,信号处理和文件锁。有关线程共享的进程状态的完整列表,您需要查看特定线程实现的文档。例如,pthreads手册页


4

线程有两种观点:操作系统和编程语言。在这两种情况下,线程具有的属性都有一些差异。

线程的最小定义是,事物是顺序发生的,一件又一件地发生。

在典型的机器执行模型中,每个线程都有自己的一组通用寄存器和自己的程序计数器。如果机器将特定的寄存器设置为堆栈指针,则每个线程有一个副本。

从操作系统的角度来看,操作系统支持线程所要做的最少工作就是提供一种在线程之间进行切换的方法。这既可以自动发生(强制性多任务处理,也可以仅在线程发出显式请求时才发生(协作性多任务处理;在这种情况下,线程有时被称为纤维)。也存在具有抢占和协作收益的混合模型,例如不同组的线程之间的抢占或任务,但在相同组/任务的线程之间有明确的收益。在线程之间进行切换至少要保存旧线程的寄存器值并恢复新线程的寄存器值。

在提供任务之间隔离的多任务操作系统中(或进程,您可以将这些术语视为OS上下文中的同义词),每个任务都有自己的资源,特别是地址空间,但也有打开的文件,特权等。由操作系统内核提供,它是进程之上的实体。通常,每个任务都有至少一个线程-不执行代码的任务用处不大。操作系统可能会或可能不会在同一任务中支持多个线程。例如原始的Unix没有。任务可以通过安排在多个线程之间切换来运行多个线程-这不需要任何特殊特权。这称为“ 用户线程””,尤其是在Unix环境中。如今,大多数Unix系统确实提供内核线程,特别是因为它是使同一进程的多个线程在不同处理器上运行的唯一方法。

除计算时间外,大多数操作系统资源都附加到任务而不是线程上。某些操作系统(例如Linux)明确定义了堆栈的界限,在这种情况下,每个线程都有自己的线程。但是在某些操作系统中,内核对堆栈一无所知,就它们而言,它们只是堆的一部分。内核通常还为每个线程管理内核上下文,内核上下文是一种数据结构,其中包含有关线程当前正在执行的操作的信息。这使内核可以同时处理系统调用中阻塞的多个线程。

就操作系统而言,任务的线程运行相同的代码,但是在该代码中的不同位置(不同的程序计数器值)。程序代码的某些部分始终在特定线程中执行可能会或可能不会发生,但是通常可以从任何线程调用通用代码(例如,实用程序函数)。所有线程都看到相同的数据,否则它们将被视为不同的任务;如果某些数据只能由特定线程访问,则通常仅是编程语言的权限,而不是操作系统的权限。

在大多数编程语言中,存储在同一程序的线程之间共享。这是并行编程的共享内存模型。它非常流行,但也很容易出错,因为当竞争条件发生时,多个线程可以访问相同的数据时,程序员需要谨慎。请注意,甚至局部变量也可以在线程之间共享:“局部变量”(通常)是指名称仅在一个函数执行期间有效的变量,而另一个线程可以获取指向该变量的指针并对其进行访问。

还有一些编程语言,其中每个线程都有其自己的存储,它们之间的通信是通过在通信通道上发送消息来进行的。这是并发编程的消息传递模型。Erlang是专注于消息传递的主要编程语言;它的执行环境对线程的处理非常轻量级,并且它鼓励使用许多寿命短的线程编写的程序,而与大多数其他编程语言相反,在大多数其他编程语言中,创建线程是一项相对昂贵的操作,而运行时环境不能支持非常大的线程同时的线程数。Erlang的顺序子集(发生在线程中的语言部分,特别是数据操作)(主要)纯粹是功能性的;因此,一个线程可以将消息发送到包含某些数据的另一个线程,并且两个线程都不需要担心该线程在使用该数据时会修改该数据。

某些语言通过提供线程本地存储(有或没有类型系统来区分线程本地存储位置与全局代码存储区)来混合这两种模型。线程本地存储通常是一项便利功能,它允许变量名称在不同线程中指定不同的存储位置。

一些(困难的)后续操作可能对了解什么是线程感兴趣:

  • 内核支持多个线程所需要做的最少工作是什么?
  • 在多处理器环境中,将线程从一个处理器迁移到另一个处理器需要什么?
  • 在没有操作系统支持且不使用内置支持的情况下,以自己喜欢的编程语言实现协作多线程(协程)会怎样?(请注意,大多数编程语言都缺少在单个线程内实现协程的必要原语。)
  • 如果编程语言具有并发性但没有线程的(显式)概念,它将是什么样?(主要示例:pi演算。)

这是我几个月来读过的最有趣的东西。
JSON

2

那要看。如果您认为线程是由POSIX(由Unix系统提供)或Windows(不熟悉后者,则必须专门询问)定义的,则给出的答案(基本上如@WanderingLogic答案所解释)。Linux使用非标准clone(2)系统调用有其自己的线程概念。它提供了对父母和孩子共享内容的相当精细的控制。它竟把具有fork(2)vfork(2)基本围绕内包装clone(2),具有特定的标志调用它,也就是说,你可以创建“线程”这一比例微乎其微与父。查找其手册页面以获取详细信息,它们可以在线获得,例如此处。是的,Linux确实提供了POSIX风格的线程,但是除此之外。


0

线程共享:

  • 地址空间
  • 静态数据
  • 代码段
  • 文件描述符
  • 全局变量
  • 子进程
  • 待处理的警报
  • 信号和信号处理程序
  • 会计信息

线程有自己的:

  • 程序计数器
  • 寄存器
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.