首页 > 操作系统 > 互不关联的多线程

互不关联的多线程

2010年1月28日 行者 发表评论 阅读评论

  我们在前面曾经说过,当多个互相独立的处理算法对同一个共享的数据结构进行处理的时候使用多线程是有用的。严格地说你也可以使用多个进程(每个进程有一个线程)来共享数据,有些情况下使用同一个进程中的多个线程来处理会更简单些。下面说说在哪里以及为什么要使用多线程。

  我们的例子里,我们会使用一个标准的输入/处理/输出的模型。通常来说,这个模型的一部分负责从什么地方获取输入,另外一部分负责处理输入并产生某种形式的输出,第三部分则是将输出反馈到什么地方。

多进程

  先来看看多进程、每个进程一个线程的情况。在这里,我们有三个进程,一个输入进程、一个处理进程和一个输出进程,如下面的示意图所示:

多进程

  这是一个极度抽象的形式,也是最不互相关联的。输入进程没有被处理进程或输出进程约束,它只是负责收集输入并使用一定的方法将输入传给下一个阶段(处理阶段)。对于剩下的两个进程也是一样的,它们对彼此没有实际的约束。我们也假设它们之间的通信通道(输入到处理、处理到输出)是通过某种链接协议(例如,管道、POSIX消息队列或实时系统的原生消息传递)完成的。

多进程共享内存

  根据数据流的流量,我们需要对通信通道进行优化。最简单的优化方法就是将这三个进程联接的更紧密些。我们不选择通用的连接协议,现在我们使用共享内存的方案,如下面的示意图所示:

共享内存

  在这里,我们让它们的连接更紧密了,最终获得的就是更快与更有效率的数据流。我们仍然可以使用通用协议来传输控制信息——因为控制信息不会消耗太多的带宽。

多线程

  最紧密的连接如下面的示意图所示:

多线程

  这里的一个进程中有三个线程。这三个线程绝对的共享数据区。同样,控制信息可以像前面所说的那样实现,或者通过某种线程同步元素(互斥体、壁垒、信号量等等)来加以实现。

比较

  现在,我们从不同方面对上面的三种方法加以比较,同样也会说说其中的牺牲。

  在系统1中,连接是最松散的。优点是这三个进程可以容易的用其他模块替换(通过命令行而不需要重新编译或设计)。这也是自然的,因为模块化的单元就是整个模块自己。系统1也是这三种中唯一一个可以在一个N网络的多个节点分布的系统。由于通信通道可以抽象为某种连接协议,这三个进程就可以在网络中的任何一个计算机上运行。这个设计的元素中最强大的就是其可伸缩性——可以让你的系统在地理上分布的数百台计算机上运行并互相通信。

  一旦牵涉到共享内存,我们就失去了在网络上分布式运行的能力。N系统是不支持网络分布式的共享内存对象的。所以在系统2中,我们实际上就限制我们只能在同一台计算机上运行这三个进程。不过我们还没有失去简单的移除或替换模块的能力,因为我们还是使用了互相分离的进程,并且这些进程可以在命令行控制。不过对这些可删除模块增加了必须符合共享内存模型的限制。

  在系统3中,我们丧失了上面的所有能力。我们绝对不可能在多个节点上运行同一进程中的多个不同线程(尽管我们可以在一个SMP系统的不同处理器上运行它们)。我们也没有了可配置的能力——现在我们必须明确定义我们要使用的输入、处理与输出的算法。

  那么我们为什么要像系统3那样设计我们的系统呢?为什么不选择像系统1那样的灵活性最高的系统?

  尽管系统3是最不灵活的,不过它可能是运行最快的。在这里没有不同进程中的线程间的环境变量切换,就不需要明确的设定内存共享,也不需要使用抽象的同步机制(例如管道、POSIX消息队列等)来传递数据或控制信息——我们只需要使用基本的内核级线程同步元素。另外一个优点就是只有一个进程的系统启动之后,我可以确定我所需要的全部东西已经从存储介质中载入了。最后,系统3也可能是最小的,因为没有三个独立的进程信息的拷贝。

  总结一句就是,知道每种系统的牺牲与妥协是什么,使用能解决问题的方法。

Related posts:

  1. 线程池(Pools of threads)
  2. 在单CPU上使用多线程
  3. 用于线程同步的条件变量(Condition Variables)
  4. 实际系统中的调度
  5. 用于线程同步的读写锁(Readers/writer locks)

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.