Conner's profile☆ Conner Wang ☆PhotosBlogListsMore Tools Help

Blog


    October 02

    多处理器环境下不出错而单处理器环境下却出错的多线程程序

    多线程程序比单线程程序要考虑的东西要多的多,如临界区、竞争、死锁、资源的维护等等。今天我写程序的时候就犯了一个低级的错误,从而导致我的程序在多CPU环境下可以运行而在单CPU环境下却出问题。

    程序是用VC写的,共有两个线程:一个是GUI线程,一个是数据处理线程。处理线程会产生日志信息,日志信息会经过两种方式被处理:1. 通过发消息从而在GUI界面里增量显示;2. 在处理线程中将日志信息增量写入日志文件。

    我的日志信息是一个CString,它是在堆里动态分配的。我最初的程序是将日志信息的地址发给GUI线程,GUI线程显示完毕后释放这部分内存。后来我又加上了日志写入文件这一功能,就是日志信息在GUI界面显示完毕后还会在处理进程中写入日志文件。

    由于我的开发环境是多核环境,一开始并没有什么问题,但当我将程序运行在单核机器上时,有时会出问题,说是某个引用的地址0x00000000无效。经过调试我才发现问题出在内存释放这一部分:在我的多核系统中,日志信息在两个线程中同时被使用,由于GUI进程处理的稍慢,所以当它释放内存时,处理进程已经使用完,所以不出错;而在单核系统中,同时只有一个进程在运行,如果写入日志文件的操作晚于GUI线程释放内存的操作,则系统会出现问题。

    找到了问题的根源所在,修改就很简单了,方案有两种:
    1. 将日志信息先在处理进程中写入文件,然后再发消息给GUI进程来显示
    2. 将日志信息统一在GUI进程处理(显示并写入日志)

    教训:
    通常多线程程序在单CPU系统中不容易出现问题而在多CPU系统中才出现问题,但并不总是如此,我这就是一个例子。

    March 22

    并行与分布式计算中的一些概念

        最近在看一些并行与分布式计算的一些书籍和资料,发现不同的文献对一些概念的描述并不是一致的。我就自己的理解汇总和整理了一 些想关概念。

    串行(Sequential)计算: 一个处理器依次执行指令,单就一个线程而言,本质上是串行计算的。
    并发(Concurrent)计算: 时分复用一个处理器,在单处理器上,分时、多任务操作系统采用并发计算的调度方法。
    并行(Parallel)计算: 空间复用多个处理器。

    Flynn分类法
    根据指令流和数据流的不同组织方式,通常把计算机系统的结构分为:
    (1)单指令流单数据流(SISD)
    绝大多数串行机属于该类型。
    (2)单指令流多数据流(SIMD)
    如阵列处理机,向量机。
    (3)多指令流单数据流(MISD)
    严格意义上来说没有相应的实现。
    (4)多指令流多数据流(MIMD)
    包括共享存储MIMD多处理机,分布式存储MIMD多处理机。

    并行计算机系统绝大部分为MIMD系统,如:
    (1)并行向量机(PVP, Parallel Vector Processor)
    (2)对称多处理机(SMP, Symmetric Multiprocessor)
    (3)大规模并行处理机(MPP, Massively Parallel Processor)
    (4)机群(Cluster)
    (5)分布式共享存储多处理机(DSM, Distributied Shared Memory)

    并行程序进程间通信的方式可分为:共享存储器方式、消息传递方式。
    对程序员而言,消息传递范例不如共享存储器范例那样有吸引力。于是就有了分布式共享存储器系统。在这种系统中,存储器物理分布在每个节点中,但是每个处理器使用统一的地址空间对整个存储器进行访问,从而隐藏了存储器是分布的这上事实。这样程序员就可以以共享存储的方式来写算法。

    我认为最好用一种库来支持并发性,并且该库不需要主语方扩展就能够实现。
                                                           --C++之父Bjarne Stroustrup

    C++并行与分布式编程标准:
    OpenMP 一种共享存储模型下编程标准。
    MPI(Message Passing Interface) 为超大规模并行计算机和工作站集群的高性能而设计。
    POSIX Unix编程标准。
    PVM(Parallel Virtual Machine) 是一个软件包,它允许一个异构计算机集能过网络连接在一起,使用起来如同一个单独的大规模并行计算机。
    CORBA 是分布式跨平台面向对象编程的标准。

    集中式处理系统: 某种处理任务被集中到一个处理单元上,系统为星型结构,一般只有一个核心处理单元;即使核心处理单元有双机备份, 也属于集中式处理系统。

    分布式处理系统: 某种处理任务被分解到多个处理器上(通常是较粗粒度的并行),系统为扁平结构,一般上层有一个控制中心,下层有多个 处理单元,通过下层多个处理单元的数量/功能扩展,来提高整个系统的处理性能。处理任务有两种分解方法:一种是同类处理任务的分解, 属于数量扩展;另一种是按功能不同进行分解,属于功能扩展;两种分解方法一般都混合使用。

        我个人认为分布式计算与并行计算的本质是一样的,或者说分布式计算是一种特殊的并行计算。从任务划分和消息的传递上,分布式计算都要比并行计算粒度要粗。有人说分布式计算是进程级别上的协作,而并行计算是线程级别上的协作,也有一定道理。

    参考:
    [1] 并行算法的设计与分析. 陈国良等 2002
    [2] 并行算法实践. 陈国良等 2004
    [3] Multi-Core Programming. [孟]Shameem Akhter [美]Jason Roberts著 2007
    [4] Parallel Programming Barry, Wilkingson Michael Allon 2004
    [5] Distributed Algorithms. Nancy A. Lynch 2001
    [6] C++并行与分布式编程 Cameron Hughes Tracey Hughes 2004

    March 21

    计算机体系结构中所指的并行性

    计算机体系结构中所指的并行性(Parallelism),是指计算机系统具有可以同时进行运算或操作的特性,包括同时性并发性两种含义。
        同时性--两个或多个事件在同一时刻发生。
        并发性--两个或多个事件在同一时间间隔发生。
    提高并行性的措施:
        时间重叠 如流水线技术。
        资源重复 设置多个硬件资源。
        时间重叠+资源重复 同时使用以上两种技术。
        资源共享 这是一种软件的方法,使多个任务按一定时间顺序轮流同时使用一套硬件设备。例如多道程序、分时系统。

    February 26

    OpenMp基本原理

        最近在研究共享存储模型下的并行程序设计,找了一些书籍和相关资料来看,重点看了OpenMp官方标准2.5版。

        OpenMP是共享存储模型下并行程序设计这一领域最为流行的一个标准,它是一个跨平台、跨编译器、高可移植性的方案,而且现在已被大多数主流厂商所支持并实现。

        OpenMp规范包括三个方面:编译器制导语句(Compiler Directives)、运行库(Runtime Library Routions)、环境变量(Environment)。OpenMP依赖用户指定编译器和运行时系统的操作以实现程序的并行运行。OpenMP兼容的实现不要求对依赖性、冲突、死锁、竞争和程序不确定性导致的问题进行检测。用户必须自己保证应用程序的正确性。

        OpenMP API采用分支-合并(fork-join)模型来实现并行执行。OpenMP程序执行时的初始线程是串行执行的。当一个线程遇到一个并行结构(parallel construct)时,这个线程将创建一个包含自身在内的线程组(team),并且自己变成这个线程组的主线程(master thread)。这个新的线程组中的所有线程执行这个并行结构中的代码。在这个并行结构的最后隐含有一个屏障(barrier)。只有主线程才能执行并行结构以后的代码。一个程序中可以指定任意数目的并行结构,并且允许嵌套。

        OpenMP提供一个松一致性(relaxed-consistency),共享内存(shared-memory)的模型。所有的OpenMP线程可以检索或存储主存中的变量。另外,每个线程允许有自己对主存的临时视图(temporary view)。临时视图并不是OpenMP内存模型所必须的,但这个概念可以表示多种实际中可能介入在线程与主存之间的结构,例如寄存器、缓存或者其它的局部存储器等。临时视图允许线程缓存变量而不必每次都访问主存。

        parallel制导语句可以决定相关并行结构中变量的可访问性,变量可以是共享的(shared)和私有的(private)。并行结构中访问的共享变量和并行结构外的原始的共享变量是同一个版本。而对于私有变量,每个参予该并行结构的线程(除了该线程组的主线程)都将在主存中复制一个该变量的副本。对于私有变量的访问,都是对当前线程中该变量的私有版本进行访问。   

        内存模型之所以具有松一致性是因为于每个线程的临时视图并不总是与主存一致。如果多个线程在没有同步机制的情况下修改和检索同一个变量,则产生的结果是未知的。OpenMP的刷新操作(flush opteration)强制临时视图与主存保持一制性。