操作系统---若干线程问题

5.3.1 系统调用fork和exec

前面有所讨论过系统调用 fork 如何用于创建独立的、重复的进程。在多线程程序中,系统调用fork和exec的语义变化。如果程序中的一个线程调用fork,那么新进程会复制所有线程还是新进程只有单个线程? 有的UNIX系统有两种形式的fork, 一种复制所有线程,另一种只复制调用了系统调用 fork的线程。 系统调用exec 的工作方式与第四章所述的方式通常相同。 即, 如果一个线程调用了系统调用exec, 那么exec参数所指定的程序会替换整个进程,包括所有线程和 LWP。

fork的两种形式的使用与应用程序有关。如果调用fork之后立即调用 exec,那么复制所有线程就没有必要,因为exec 参数所指定的程序会替换整个进程。在这种情况下,只复制调用线程比较恰当。不过,如果在fork之后另一进程并不调用exec, 那么另一进程就应复制所有线程。

5.3.2 取消

线程取消(thread cancellation)是在线程完成之前来终止线程的任务。例如, 如果多个线程并发执行以搜素数据库 并且一个线程返回了结果,那么其他线程就可被取消。另一情况是用户按下网页浏览器上的按钮以停止进一步装入网页。通常装入网页是由一个独立线程进行的。当用户按下 停止按钮时,装入网页的线程就被取消了。
要被取消的线程通常称为 目标线程。目标线程的取消可在如下两种情况下发生:
1、异步取消(asynchronous cancellation):一个线程立即终止目标线程。
2、延迟取消(deferred cancellation):目标线程不断地检查它是否应终止,这允许目标线程有机会按着有序方式来终止自己。
如果资源已分配给被要取消的线程或要取消的线程正在更新与其他线程所共享的数据,那么取消在这些情况下就会有困难。对于异步取消而言这回变得尤其麻烦。操作系统通常收回取消线程的系统资源,但是往往并不回收所有资源。因此,异步取消线程并不会使所需的系统资源空闲。

另外,一个线程显示目标线程要被取消时,就会发生延迟取消。不过,只要当目标线程检查以确定它是否应该取消才会发生取消。这允许一个线程检查它是否是在安全的点被取消。Pthread API 的操作系统允许延迟取消。

5.3.3 信号处理

信号在UNIX 系统中用做通知进程某个特定时间已经发生了。根据来源和被通知信号的事件理由,信号可以被同步或异步接收。不管信号是同步还是异步的,所有信号具有同样模式:

  1. 信号是由特定事件的发生所产生的。
  2. 产生的信号是发送到进程。
  3. 一旦发送,信号必须要加以处理。

同步信号的例子包括非法内存访问或被零所除。在这种情况下,如果运行程序执行这些动作中的任何一个,那么就产生信号。同步信号发送到执行操作而产生信号的同一进程(这就是为什么被认为是同步的)。
当一个信号是由运行进程之外的事件所产生,那么进程就异步地接收这一信号。这种信号的例子包括用特殊键(比如 Ctrl + C)或定时器时间到期以终止进程。通常,异步信号被发送到另一个进程。
每个信号可能由两种可能的处理程序中的一种来被处理:

  1. 默认信号处理器。
  2. 用户定义的信号处理程序。

每个信号都有 默认信号处理程序(default signal handler),这是当处理信号时由内核运行的。这种默认动作可以 用户定义的信号处理程序函数来改写。在这种情况下,用户定义函数被调用用来处理信号,而不是默认的动作。在这种情况下,用户定义函数被调用用来处理信号,而不是默认的动作。同步和异步信号可按不同方式被处理。有的信号可以被忽略(如改变窗口大小);其他的可能要通过终止程序来处理(如非法内存访问)。
单线程程序的信号处理比较直接;信号总是发送给进程。不过,对于多线程程序,发送信号就比较复杂,因为进程可能有多个线程。那么信号应被送到哪里呢?
通常存在如下选择:

  1. 发送信号到信号所应用的线程。
  2. 发送信号到进程内的每个线程。
  3. 发送信号到进程内的某些线程。
  4. 规定一个特定线程以接收进程的所有信号。
    发送信号的方法依赖于所产生信号的类型。例如,同步信号需要发送到产生这一信号的线程,而不是进程中的其他线程。不过,对于异步信号,情况就不是那么清楚了。有的异步信号如终止进程的信号(如Ctrl+C)应该发送到所有线程。有的多线程版UNIX允许线程描述它会接收什么信号和拒绝什么信号。因此,有些异步信号只能发送给那些不拒绝它的第一个线程。 Solaris 2 按第四种方法处理:它在每个进程内创建了一个专门只处理信号的线程。当异步信号被发送到一个进程时,它被发送到该特定线程,进而再将信号传递给第一个不拒绝他的线程。
    虽然Windows 2000并不明确地提供对信号的支持,但是它们能通过 异步过程调用(asynchronous procedure, APC)来模拟。APC工具允许用户线程指定一个函数以便在用户线程收到特定事件通知时能被调用。正如其名成所表示的,APC与UNIX 的异步信号大体相当。不过, UNIX需要力争解决如何处理多线程环境的信号,而APC机制则较为直接,因此,APC只发送给特定线程而不是进程。

5.3.4 线程池

在5.1节描述了对网络服务器进行多线程编程的情况。在这种情况下,每当服务器收到请求,它就创建一个独立线程以处理请求。虽然创建一个独立线程当然要比创建一个独立进程要好,但是多线程服务器也有一些潜在问题。第一个是关于在处理请求之前用以创建线程的问题,以及线程在完成其工作之后就要被丢弃这一事实。第二个问题更为麻烦:如果允许所有并发请求都通过新线程来处理,那么并没有限制在系统中并发执行的线程数量。
无限制的线程会用尽系统资源如CPU时间或内存。对这个问题的解决方法之一是使用线程池(thread pool)。
线程池的主要总体思想是在进程开始时创建一定数量的线程,并放入到 池中以等待工作。
当服务器收到请求时,它会唤醒池中的一个线程(如果有可用的线程),并将要处理的请求传递给它。一旦线程完成了它的服务,它会返回到池中等待更多工作。如果池中没有可用的线程,那么服务器会一直等待直到有空线程为止。

具体来说,线程池有如下优点:

  1. 通常用现有线程处理请求要比等待创建新的线程要快。
  2. 线程池限制了在任何时候可存在线程的数量。这对那些不能支持大量并发线程的系统尤为重要。
    池中线程数量可以通过一些因素如系统中CPU的数量、物理内存的大小和并发客户的请求的期望值来启发地设置。更为高级的线程池的体系结构能动态调整池中的数量以适应使用情况。这类体系结构提供了较小池 的有一个好处,即在系统负荷低时减低内存消耗。

5.3.5线程特定数据

同属一个进程的线程共享进程数据。事实上,这种数据共享提供了多线程编程的一种好处。不过,在有些情况下每个线程可能需要一定数据的自己的拷贝。这种数据称做线程特定数据(thread-specific data).。例如,对于事务都有一个唯一标识符。为了让每个线程与其唯一标识符相关联,可以使用线程特定数据。绝大多数线程库,包括Win32 和Pthread,都提供了对线程特定数据的一定形式的支持。Java也提供这种支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值