多线程入门

分布式操作系统中多线程入门

你好!本篇是一些多线程的基础知识,是分布式操作系统入门的一部分内容,如果你感兴趣,就继续看下去吧~
事先说明,这写的像坨屎,但是并未添加作者的曲解。

线程简介 Introduction to Threads

这部分就不用多说了吧,网上类似的关于线程的介绍不要太多,这里简而言之了哦。

为什么需要线程

Consider,举个?,一个文件服务器偶尔需要阻塞来等待磁盘响应。如果一个服务器有很多可以使用的线程,第二个线程在第一个线程睡眠的时候就可以run,这样调度的结果会有更高的吞吐量和更好的表现。但是通过创造两个完全独立的服务器进程来完成这样的目标是不可能的,因为它们要共享一个公共的缓冲区,而且这样的缓冲区是在同一块地址空间的。因此我们需要一个新的模式,一个在单进程操作系统里不会出现的模式,那就是我们本篇的重点:多线程

进程与线程

图1-1. (a)三个进程都只含一个线程(b)一个进程有三个线程图1-1. (a)三个进程都只含一个线程(b)一个进程有三个线程

在图1-1(a)里我们看见一个机器有三个进程,每个进程都有自己的程序计数器,堆栈,寄存器组和地址空间。每个进程和别的进程之间彼此毫无关系,除了它们可能能够通过系统的进程间通信原语进行通信,例如信号量,监视器或消息队列。

此处有很多啦,比如管道/匿名管道(pipe) 、有名管道(FIFO) 、信号(Signal) 、消息(Message)队列 、共享内存(share memory) 、信号量(semaphore) 、套接字(socket)等

在(b)中的另一个机器中,我们看到了只有一个进程,只有这个进程包含多个控制线程,通常只是称为线程,或者有时可以看作轻量级进程。在许多方面,线程就像小小的进程。每个线程严格按顺序运行,并有自己的程序计数器和堆栈,以跟踪它的位置。线程与进程一样共享CPU:第一个线程运行,然后另一个线程运行(分时)。只有在多处理器上才能实际并行运行。线程可以创建子线程,并且可以阻塞 等待系统调用完成,就像常规进程一样。当一个线程被阻塞时,同一进程中的另一个线程可以运行,与一个进程阻塞的方式完全相同,同一台机器中的另一个进程可以运行。类比:线程之于进程就像进程之于机器,在很多方面都是如此。

图1-2. 进程与线程组成
图1-2. 进程与线程组成

但是,进程中的不同线程并不像不同进程那样独立。所有线程都具有完全相同的地址空间,这意味着它们也共享相同的全局变量。由于每个线程都可以访问每个虚拟地址,因此一个线程可以读取,写入甚至完全消除另一个线程的堆栈。线程之间没有保护,因为它是不可能的也是非必需的。有些进程可能来自不同用户并且可能彼此不友好,与这些进程不同,一个进程总是由单个用户拥有,该用户可能创建了多个线程以便他们可以合作而不是冲突。除了共享地址空间外,所有线程共享同一组打开文件,子进程,定时器和信号等,如图1-2所示。因此,当三个过程基本上不相关时,将使用图1-1(a)的模式,而当三个线程实际上是同一工作的一部分并且是有相对比较密切的合作时,图1-1(b)将更为合适。

线程状态

图1-3. 进程状态图
图1-3. 进程状态图

与传统进程(即只有一个线程的进程)一样,线程可以处于以下几种状态中的任何一种:运行,阻塞,就绪或终止。正在运行的线程当前具有CPU并处于活动状态。被阻塞的线程正在等待另一个线程解除阻塞(例如信号量)。就绪的线程等待被调度运行,并且一旦轮到它就会立即运行。最后,一个已终止的线程已退出,但尚未被其父线程回收(在UNIX术语中,父线程尚未完成子线程会WAIT)。

线程使用 Thread Usage

线程的三种组织形式

Dispatcher/worker model

为了允许并行性与顺序执行和阻塞系统调用相结合,人们发明了线程。以我们的文件服务器为?,其中一个可能的组织形式如图2-1(a)所示,这里有一个调度线程(dispatcher),从系统邮箱中读取传入的工作请求,在检查请求之后,它会选择一个空闲的工作线程(即被阻塞的线程)完成请求,可能是通过将指向消息的指针写入与每个线程相关联的特殊字符,然后,这个调度线程(dispatcher)唤醒了睡眠的工作线程(dispatcher)(例如,通过在其正在睡眠的信号量上执行UP(coresponding to Wakeup,也就是唤醒))。

图2-1. 一个进程中的三个线程模式。 (a)Dispatcher/worker model(b)Team model (c)Pipeline Model
图2-1. 一个进程中的三个线程模式。 (a)Dispatcher/worker model(b)Team model (c)Pipeline Model

当工作线程被唤醒时,它会检查所有的线程都可以访问共享块缓存。如果不能,它会向磁盘发送一条消息以获取所需的块(假设它是读操作)并进入休眠状态等待磁盘操作完成。然后调度程序(scheduler)会被唤起并启动另一个线程,可能是调度线程(dispatcher),以便获得更多工作,或者可能是另一个现在可以运行的工作线程(worker)。

让我们再来考虑考虑如何在没有线程的情况下编写文件服务器。一种可能的方式是使其作为单个线程运行。文件服务器的主循环获取请求,检查后在获得下一个请求之前完成它。在等待磁盘时,服务器处于空闲状态,不处理任何其他请求。如果文件服务器在专用计算机上运行(通常是这种情况),则在文件服务器等待磁盘时,CPU总是空闲的。最终结果是单位时间只能处理很少的请求。因此,线程获得了相当大的性能,但是每个线程通常都会顺序运行。

到目前为止,我们已经看到了两种可能的设计:多线程文件服务器和单线程文件服务器。假设线程不可用,但系统设计人员发现单线程导致的性能损失是不可接受的。第三种可能性是将服务器作为大型有限状态机运行。当请求进入时,唯一的线程会检查它。如果缓存命中,很好,但如果没有,则必须将消息发送到磁盘。

但是,它不是阻塞,而是在表中记录当前请求的状态,然后继续获取下一条消息。下一条消息可以是对新工作的请求,也可以是磁盘对先前操作的回复。如果是新工作,那就开始了。如果是来自磁盘的回复,则从表中获取相关信息并处理回复。由于不允许在此处发送消息并阻止等待回复,因此无法使用RPC(远程过程调用)。原语必须是非阻塞调用发送和接收。

RPC远程过程调用是一个分布式计算的客户端-服务器(Client/Server)的例子,它简单而又广受欢迎。远程过程调用总是由客户端对服务器发出一个执行若干过程请求,并用客户端提供的参数。执行结果将返回给客户端。由于存在各式各样的变体和细节差异,对应地派生了各式远程过程调用协议,而且它们并不互相兼容。
------ weki pedia

在这个设计中,前两个案例中的“顺序过程”模型丢失了。对于发送和接收的每条消息,必须在表中明确保存和恢复计算状态。实际上,我们正在艰难地模拟线程及其堆栈。该过程作为有限状态机运行,获取事件然后根据其中的内容作出反应。

现在我们应该清楚线程必须提供什么了。它们不仅保留了阻塞系统调用的顺序进程(例如,RPC与磁盘通信),并且有效实现了并行。
使得保留了阻塞系统调用的顺序进程(例如,RPC与磁盘通信)并仍然实现并行性的想法成为可能。阻塞系统调用(blocking system call)使编程更容易,具有并行性从而提高了性能。单线程服务器保留了阻塞系统调用的便利性,但却放弃了性能。有限状态机方法通过并行性实现了高性能,但使用的是非阻塞调用,因此难以编程。这些模型总结在表2-1中。

A blocking system call is one that must wait until the action can be completed. read() would be a good example - if no input is ready, it’ll sit there and wait until some is (provided you haven’t set it to non-blocking, of course, in which case it wouldn’t be a blocking system call). Obviously, while one thread is waiting on a blocking system call, another thread can be off doing something else.

模型特点
线程(Threads)并行阻塞系统调用
单线程进程(Single-thread process)不并行,阻塞系统调用
有限状态机(Finite-state machine)并行,非阻塞系统调用

表2-1. 模型及其特点

Team model

图2-1(a)的调度程序结构并不是组织多线程进程的唯一方法。图2-1(b)的团队模型(Team model)也是一种可能性。Team model中所有线程都是相同的,每个线程都获取并处理自己的请求,没有调度员。有时工作是线程无法处理的,特别是如果每​​个线程专门用于处理特定类型的工作。在这种情况下,可以维护作业队列,并将待处理的工作线程保留在作业队列中。使用这种组织形式,线程应在查看系统邮箱之前检查作业队列。

Pipeline Model

线程也可以以图2-1©的流水线模型(Pipeline Model)运行。在此模型中,第一个线程生成一些数据并将它们传递给下一个线程进行处理。数据从线程到线程继续,每个步骤都在进行处理。虽然这不适合文件服务器,但对于其他问题,例如生产者 - 消费者,它可能是一个不错的选择。流水线模式广泛应用于计算机系统的许多领域,从RISC CPU的内部结构到UNIX命令行。

生产者 - 消费者(待扩展)

Threads for Clients

线程通常也对客户端有用。例如,如果客户端希望在多个服务器上复制文件,则可以与每个服务器进行一次线程通信。客户端线程的另一个用途是处理信号,例如来自键盘的中断(DEL或BREAK)。一个线程专门用于等待信号,而不是让信号中断过程。通常情况下,这个特殊的线程被阻塞,但当信号进入时,它会唤醒并处理信号。因此,使用线程可以优化对用户级中断的处理。

线程的使用场景

线程的另一个争议与RPC或通信无关。有些应用程序更容易使用并行进程编程,例如生产者 - 消费者问题。生产者和消费者是否实际并行运行是次要的。它们以这种方式编程,因为它使软件设计更简单。由于它们必须共享一个公共缓冲区,因此将它们放在独立的进程中是不行的,线程就更为适合此处场景。

线程使用小结

最后,虽然我们没有在这里明确讨论这个主题,但在多处理器系统中,单个地址空间中的线程实际上可以在不同的CPU上并行运行。事实上,这是在这些系统上进行共享的主要方式之一。另一方面,一个合理使用线程的设计程序应该在分时处理多线程的单CPU或者真正的多核处理器上具有相同的运行结果,因此软件问题无论如何都应该以同种方式进行。

未完待续…嘿嘿嘿

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值