现在的位置: 首页 > 综合 > 正文

IBM的ZThread库

2012年08月15日 ⁄ 综合 ⁄ 共 20541字 ⁄ 字号 评论关闭

下载地址

 

去除调试信息:

在Debug.h中的最前端增加#define ZTDEBUG(x),忽略警告,DONE

 

这个库用起来应该比Linux系统下提供的并发的借口要来的舒坦,不知道有没有TBB快速,但是可以肯定的是为程序员省掉了很多代码的编辑工作。

 

Mark一下,认真学习之。

 

 

当需要使用多线程库的时候,需要从Runnable共有继承产生一个类,然后重新定义run()函数

代码如下:

 

Code1-1

 

Synchronization_Exception是ZThread库的一部分,是该库处理异常的一个类,当出错时会抛出该类的对象

 

当创建了一个Thread对象的时候,相关联的线程就会在线程处理系统内注册,并保持活动状态。

 

以下是一个响应UI的例子:

Code1-2

 

该例子也表明了任务之间需要通信。当药销毁一个任务时,有任务自己决定什么时候停止是最安全的——设置一个布尔标记,来通知任务停止。

 

使用执行器(Executor)简化工作

执行器在客户端和任务的执行之间提供了一个间接层,客户不再直接执行任务,而是由中间的对象来执行。

Code1-3

 

 

对比Code1-3和Code1-1可以看出差别——1-3中不再需要一个Thread的对象了(意味着不再直接调用线程?),用一个中间对象ThreadExecutor代替之。

 

ThreadExecutor的缺陷:需要为每一个任务都创建线程,由于创建了过多线程会导致过多的开销。

                                   必须用try包裹,因为出错会抛出Synchronization_Exception的对象

 

在某些情况下可以用单个Executor对象来创建和管理所有的线程(什么情况?)

 

当ThreadExecutor开销过大的时候使用PoolExecutor来代替

 

使用有限的线程集用并行的方式提交任务:代码代码~~

 

Code1-4

 

ConcurrentExecutor类和PoolExecutor类类似,该类只有一个线程,常用于长期处于活动状态的任务(例如监听套接字),

或者短任务,例如更新日志。当有多个任务提交给该对象时,所有的任务都是串行化执行。

 

和ConcurrentExecutor类似,SynchronizationExecutor用于需要同一时刻只运行一个任务的时候,串行代替了并发。

但是该类不会创建和管理线程,它只使用提交任务的线程,因此只会作为同步的焦点(?)

 

优先级的设置:

不能使用Executor的中间对象,必须使用Thread对象(需要通过线程来设定优先级)

优先级分为三级(High,Low,Medium)

 

保证对象的存在以及共享资源:

必须使用new来创建对象(使之存在于内存堆上),创建完成之后,不必显式的删除对象(任务会完成删除的工作),因为涉及

到引用计数的问题,该工作只能由其完成!!!

 

补充一点关于volatile的知识:

需要使用volatile的地方:

  1). 并行设备的硬件寄存器(如:状态寄存器)

    2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

      3). 多线程应用中被几个任务共享的变量

      一个参数既可以是volatile也可以是const

      指针可以是volatile类型

      优化和多线程之间的折中办法

     1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

     2、多任务环境下各任务间共享的标志应该加volatile;

     3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

    另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),

    在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

 

抽象类一般都用于设定一个统一的接口,例如ZThread中的Runnable和Cancelable两个类,其中的函数均为纯虚函数。

 

依赖于Cancelable的对象的所有任务都要测试这个标志来检查该对象是否被取消。

 

访问控制的Mutex机制:

需要使用的两个函数,lock.acquire();lock.release();

死锁的产生--如果从临界区中返回,没有释放该锁,将阻止其再次从临界区获得该锁。

 

使用保护简化代码的工作量(因为如果单独使用Mutex对象,需要对每一个对象执行acquire和release操作),而Guard

模板的实现,大量的简化了这个的工作量;

只需要在加锁数据的时候使用Gurad<Mutex>gm(lock);

之后完全不用操心什么时候释放这个锁。

 

关于Guard:

1. Guard也可以用来临时解锁一个保护(Code1-5)

2. Guard可以尝试在某一个确定的时间获得某个锁,然后放弃(Code1-6)

   在这里要注意的就是如果在时间内不能获得锁,将会

 

                                                                                     Code1-5

 

                                                                                      Code1-6

 

同步整个类:

使用模板GuardClass,这样能确保在任意时刻一个类对象只有一个===函数===在运行

 

关于线程安全性:

代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。摘自百度百科

 

另外,在头文件threadlock中源代码需要做一个略微的修正:

        const Value& operator=(const T& v) { value = v; return *this;}//modified by HIT
        const Value& operator=(const T& v) { value = v; }

否则会报没有返回值的错误

 

原子操作:

在一个微处理器指令中完成并且不能够被中断,例如返回一个int值或者++num的操作,多处理器上的原子操作更为复杂。

 

一个线程的4种状态:

1。 新建(CPU肯定会为其分配时间片)

2。 运行

3。 阻塞

4。 死亡

 

线程被阻塞的4个原因:

1。sleep函数,使其在限定时间内被阻塞

2。等待I/O操作的完成

3。等待互斥的数据

=======

4。使用wait()挂起了该线程,只有等到broadcast()或者signal()信号才能继续执行

=======

 

有时候需要在线程处于阻塞状态下停止运行(避免死锁的发生?I guess so),因此引入中断的概念

 

从一个run()函数中非正常跳出(未达到检测isCancel的部分),会很麻烦,需要销毁与之相关的对象或者释放一些资源,这样的推出和异常

更为类似,在ZThread中,使用了Interruption的异常处理。

除了IO操作,一个线程可以从任何===阻塞====操作中中断出来。

 

互斥阻塞的代码有问题,一直没有中断(待解决)

 

 

死锁发生的条件:

1。 相互排斥,线程使用的资源至少有一个是不被共享的

2。 至少有一个进程必须持有一种资源,而且需要等待获得被其它进程所持有的资源

3。 不能以抢占的方式获得另外进程的资源,所有进程只能把释放资源当成一个正常方式

4。 出现循环等待

 

线程间的协作:

使用wait()以及condition类(使用互斥锁并且允许挂起任务)

                                                                                                  code1-7

 

几个需要注意的地方:

 

1. 处理的对象包括一个condition,该condition需要用对象的互斥锁来初始化。
2. 在等待下一项操作时,需要使用

                                          while(condition)
                                                  condition.wait();
3. 当产生了下一项操作所需的资源时,需要condition来调用signal()或者broadcast()函数来唤醒被挂起的进程。

 

从上述的代码中总结的一些要点:

1. 上一道工序总是持有下一道工序的指针。(为了调用其成员用来通讯)
2. 在下一道工序中,包含上一道工序是否完成的bool变量,并且包含等待上一道工序完成的成员函数。
3. 上一道工序完成之后,给下一道工序发送完成的消息,下一道工序的run()函数中有测试上一道工序是否完成的condition。

 

用队列解决线程处理的问题:

需要对任务进行串行化,即有序的处理任务,使用队列可以采用同步的方式访问其内部元素。

 

代码如下:

                                                                                                 Code1-8

 

                                                                                             Code1-9

LiftOffRunuer可以忽略同步问题,因为这些可以由TQueue解决。

1。 在每个Runnable类中代码的数量和复杂性通过队列TQueue的使用都会显著减少,保护通信等待等等操作都由TQueue

来完成,Runnable类中不在含有任何的Mutex和Condition对象。

2。 类之间的耦合程度降低到最小程度,每个类只和其TQueue通信。

 

                                                                                               Code1-10

 

对比Code1-8和Code1-10可以看出差别,使用TQueue的好处是显而易见的。

 

当有多个线程等待相同的一个条件时,使用broadcast()函数来代替wait()函数来执行唤醒工作。

 

一个巨长的代码例子:

                                                                                          Code1-11

 

关于死锁,记住产生死锁的四个条件,破坏其中的一个就能解决出现死锁的问题。

 

至此结束,任重道远~~

 

抱歉!评论已关闭.