Microsoft Word - 多线程编程.doc

Size: px
Start display at page:

Download "Microsoft Word - 多线程编程.doc"

Transcription

1 多线程编程技术 线程是比进程更小的单位, 可以认为进程是由一个或多个线程组成的 据说以前的 400 版本并不支持真正的多线程技术, 在 4.2 版后才从内核上提供了对多线程的支持 总之写这份文档的时候, 绝大部分版本应该可以支持 主要资料来源于 IBM 信息中心的 Programming Multithreaded applications, 加上部分个人观点 多线程编程有以下几点特殊性 ( 说好听点叫特殊性, 说得直接一点完全就是为什么不使用多线程技术的理由, 按理由的充分性, 由小到大排列 ): 1. 多线程的编程在对 C 程序的使用上要特别小心, 详细内容见调用 C 程序的注意事项 2. 事务处理的作用范围是 JOB 级, 或活动作业组级 这也就是说多线程并发时, 一个线程的 COMMIT 操作可能导致另一个线程也执行了 COMMIT 操作 ( 这是 IBM 说的, 不是我的猜想 ) 所以实际上也就可以认为多线程的并发不能支持事务操作 3. 对于用户的应用程序来说, 大部分 C RPG RPGLE 编写的 PGM 和 MODULE 都不具备线程安全性, 也就是不能被一个进程下的多个线程同时调用, 要注意 ( 不排除是因为某些参数未掌握好, 总之目前测试的结果就是如此 ) 同时也基于此, 多线程之下程序的复用性, 维护性就没有单线程下那么方便和自由 4. 最后, 实际测试多线程的效率, 只能用令人惊讶来形容 -- 和多进程并发效率居然几乎是一样的!-_- ( 程序写起来还麻烦得要死!) 测试方法 : 读一个 380 多万条记录的文件, 再根据某个键值去 CHAIN 另一个 620 多万条记录的文件, 仅此而已 根据这个 380 多万条记录的文件的某个关键字, 拆分成 200 多个任务, 每个任务只处理自己需要处理的数据 ( 可以简单的认为每个任务是处理 380w/210 条记录吧, 其实并非如此 ) 多进程分为 10 个进程来处理这 200 多个任务, 耗时约 3 分钟 (14:40:45 14:43:59) 多线程分为 8 个线程来处理这 200 多个任务, 耗时竟然也是约 3 分钟 (14:47:03 14:50:14), 严格的来说, 少了 3 秒钟, 我估计这 3 秒就是启动 8 个线程比启动 10 个 JOB 要少的时间吧 我在多线程里还特地增加了共享了 ODP 的处理, 居然还是只有这个效果 看到这里, 如果对多线程技术还有兴趣的话, 那就请继续往下看吧 我要早知道, 也就不写这么多了

2 目 录 1 概念 JOB Process ( 进程 ) Thread ( 线程 ) 线程的分类 线程的程序模型 JOB 和 JOB 资源 线程的私有数据和特有数据 多线程编程的环境 活动作业组 (Activation group) 基本资料 注意事项 编译时的参数 几点疑惑 调用 C 程序的注意事项 多线程编程的通讯问题 线程的数据库 数据相关处理 线程的基本管理操作 线程的属性 启动线程 结束线程 取消线程 挂起和重新运行线程 等待线程结束 让进程先处理另一个线程 线程的安全性 (Thread safety) 存储用法和线程应用 JOB 级的资源 API 的线程安全级别 CL 命令和线程安全 拒绝访问的函数和线程安全 退出点 (exit point) 多线程的程序技巧 多线程的同步 CMPSWP 互斥体 信号量 Condition variable and threads Threads as synchronization primitives Space location lock... 29

3 4.1.7 Object lock 初始化和线程安全 线程的特有数据 (thread specific data) 调用不具备线程安全性的函数 常见的多线程错误 多线程 JOB 的 DEBUG 多线程 JOB 的性能 多线程服务的建议 JOB 和线程的优先级 线程之间的冲突 线程应用中, 存储池大小设置的影响 存储池的活动级别 线程应用的性能 线程管理 API Thread management API 取线程属性 API -- Get Thread Attribute API pthread_attr_getdetachstate 取 detach 状态 pthread_attr_getinheritsched pthread_attr_getschedparam 取线程属性计划参数 设置线程属性 API -- Set Thread Attribute API pthread_attr_init 初始化线程属性 pthread_attr_destroy 销毁线程属性 pthread_attr_setdetachstate() 设置线程属性 pthread_attr_setinheritsched pthread_attr_setschedparam 取线程内容 API -- Get Thread Content API pthread_getconcurrency 取线程的并发等级 pthread_getpthreadoption_np pthread_getschedparam pthread_getthreadid_np 取当前线程的唯一标识号 pthread_getunique_np 取指定线程的标识号 pthread_self 取当前运行线程的线程描述符 设置线程内容 API -- Set Thread Content API pthread_setconcurrency 设置进程并发等级 pthread_setpthreadoption_np pthread_setschedparam 检查线程 API -- Check Thread API pthread_equal pthread_is_initialthread_np 检查当前线程是否为初始线程 pthread_is_multithreaded_np 检查当前进程是否拥有超过一个线程 线程管理 API pthread_clear_exit_np 清除线程的 EXIT 状态 pthread_delay_np 线程 DELAY pthread_detach pthread_once 执行一次初始化 pthread_trace_init_np... 42

4 7.6.6 PTHREAD_TRACE_NP sched_yield 线程操作 API -- Operation Thread API pthread_create() 创建线程 pthread_exit 结束线程 pthread_join 等待线程结束并释放线程资源 pthread_join_np 等待线程结束 pthread_extendedjoin_np 根据一些扩展条件等待线程 pthread_cancel 取消线程 线程特有数据 API Thread specific storage API pthread_key_create pthread_key_delete pthread_setspecific pthread_getspecific 取消线程 API Thread cancellation API pthread_cancel pthread_cleanup_peek_np pthread_cleanup_pop pthread_cleanup_push pthread_getcancelstate_np pthread_setcancelstate pthread_setcanceltype pthread_test_exit_np pthread_testcancel 条件变量的 API pthread_cond_init pthread_cond_wait pthread_cond_signal pthread_cond_timedwait pthread_cond_broadcast pthread_cond_destroy pthread_condattr_destroy pthread_condattr_getpshared pthread_condattr_init pthread_condattr_setpshared pthread_get_expiration_np 读 / 写锁的同步 API Read/write lock synchronization API pthread_rwlock_destroy pthread_rwlock_init pthread_rwlock_rdlock pthread_rwlock_timedrdlock_np pthread_rwlock_timedwrlock_np pthread_rwlock_tryrdlock pthread_rwlock_trywrlock pthread_rwlock_unlock... 71

5 11.9 pthread_rwlock_wrlock pthread_rwlockattr_destroy pthread_rwlockattr_getpshared pthread_rwlockattr_init pthread_rwlockattr_setpshared 其它 API -- Signal APIs pthread_kill pthread_sigmask pthread_signal_to_cancel_np 互斥体 API 互斥体操作 API -- Mutex Operation API pthread_lock_global_np pthread_unlock_global_np pthread_mutex_init 互斥体初始化 pthread_mutex_lock 互斥体锁 pthread_mutex_unlock 互斥体解锁 pthread_mutex_destroy 销毁互斥体 pthread_mutex_timedlock_np ( 带超时设置的互斥体锁 ) pthread_mutex_trylock ( 不进行阻塞处理的互斥体锁 ) 互斥体属性设置 pthread_mutexattr_init 互斥体属性初始化 pthread_mutexattr_destroy 销毁互斥体属性 pthread_mutexattr_setkind_np 设置互斥体种类属性 pthread_mutexattr_setname_np 设置互斥体属性名字 pthread_mutexattr_setpshared 设置互斥体中进程属性 pthread_mutexattr_settype 设置互斥体类型属性 pthread_mutexattr_default_np 设置互斥体为默认属性 取互斥体属性 API Mutex Attribute API pthread_mutexattr_getkind_np 取互斥体种类 pthread_mutexattr_getname_np 取互斥体属性目标名 pthread_mutexattr_getpshared 从互斥体中取出进程共享的属性 pthread_mutexattr_gettype 取互斥体类型 信号量的 API semget 带 KEY 值取信号量描述符 semop 对信号量组进行操作 semctl -- 对信号量进行控制操作 其它 spawn... 92

6 1 概念 1.1 JOB JOB, 包含了存储和其它资源这两方面的内容,JOB 自身并不能运行 (A job is a container for storage and other resources,and it cannot run by itself) 存储, 是指数据和堆栈 (Data and Stack) 其它资源, 包括环境变量 地址 文件描述 该 JOB 所打开的文件信息 当前工作目录 绝大部分 400 上的操作管理命令都是基于 JOB 的 JOB 的概念我们平时接触得很多 例如说在交互式作业中, 用户登录之后, 即是启动了一个 JOB, 系统就需要为其分配相应的资源 我们可以通过 WRKACTJOB 命令查看活动的作业 而由 USER JOBNAME JOBID 可以定位到唯一的一个 JOB, 可以使用各种系统命令, 来取得指定 JOB 当前的状态 ( 比如说 ACTIVE MSGW TIMW 等 ), 或者是 JOB 的信息 (RTVJOBA), 也可以使用各种系统命令来操作 JOB(HOLD,END 等 ) 对 JOB 的操作 管理都很直观方便 1.2 Process ( 进程 ) 进程, 是管理程序运行的资源 (Process is container of the memory and resoures of the program) 在 400 系统中, 一个 JOB 就代表了一个进程 (On IBM system i platforms, a job represent a process) 按照我的理解, 启动一个 JOB, 相当于系统就分配了相应的资源给这个 JOB,JOB 自身并不负责运行程序 ; 进程则是用来负责管理这个 JOB 下运行的程序 ( 这里的程序应该并不专指可以 CALL 的程序, 我觉得所有的人机交互应该都是通过程序来完成的, 即都是由进程来管理的 ) 按照上文所说, 一个 JOB 有且仅有一个进程 所以我个人认为在理解上, 似乎没有太大必要将进程与 JOB 刻意区分开来 一个进程可以对应多个运行中的程序 ( 比如程序 A 调用程序 B, 那么当前进程就同时管理着程序 A B 的资源 也正是基于这种原理, 所以 400 平台上的程序调用, 允许有返回数据 ) 每个进程至少拥有一个线程 ( 任务 ) 来执行程序 进程是基于 JOB 的, 我们平时所说的多进程并发, 实质上指的也就是多 JOB 并发 多 JOB 并发的原理较为简单, 就是使用 SBMJOB 命令提交作业来实现并发处理 通常为了便于管理, 会建立专用的子系统, 以及专用的 JOBD JOBQ( 这里的专用, 只是从应用级别而言, 建立的方法并没有特殊的地方 ), 也就是说, 需要启动多个 JOB 1.3 Thread ( 线程 ) 线程是程序运行的通道, 操作系统通过线程, 按照一定顺序去逐步执行程序 (A thread is the path taken by a program while running, the steps performed, and the order in which the steps are performed.) 所有的程序都拥有至少一个线程, 对于多线程的程序而言, 每一个线程都是独立于其它的

7 线程运行的 进程中运行的第一个线程, 称为初始线程 (Initial thread); 并不是所有的 JOB 都支持多线程 ( 实际上, 大部分系统应用中, 默认的 JOBD 的属性都是不支持多线程 ) 当进程对应的 Job 支持多线程时, 该进程下的其它的非初始线程的线程, 称为辅线程 (Secondary thread) 在实际操作中, 我们可以通过 WRKJOB + 20, 来查看线程的状态以及相关的线程信息 线程的分类 线程可以分为用户线程和核心线程 按照用户线程的分类, 一个进程之下的所有程序线程都共享同一个进程线程 ; 线程的 API 函数执行任务计划 (scheduling policy), 判定何时运行新的线程 任务计划决定了在一个时点上, 一个进程只能有一个活动的线程 (The scheduling policy allows only one thread to be actively running in the process at a time) 而操作系统只需要关注这个进程单一的任务 (Single Task) 按照核心线程的分类, 进程被拆分成若干个独立的任务, 一个进程对应的多个核心线程各自处理这些独立的任务 核心线程使用优先的任务计划 (preemptive scheduling policy, 这里的优先, 应该是相对于用户线程而言 ), 操作系统通过这个优先的任务计划, 判定当前由哪一个核心线程来使用处理器资源 i5 操作系统支持核心线程的处理方式 这两种分类是独立开来理解的, 也就是说当前的系统平台可能只支持用户线程, 而不支持核心线程 如果不支持核心线程的话, 那么一个进程之上即使并发了多个用户线程, 对应于操作系统而言, 仍然只是一个单一的任务, 也就是前文所说的 操作系统只需要关注的单一的任务 (Single task); 在这种情况下, 表面上并发的用户线程, 在操作系统层面其实仍然是串行的 ( 通过线程的 API 函数来分配任务 ), 这也就是前文所说的 同一时点上, 一个进程只能有一个活动的线程 的真正含义 ( 这段话是我个人的理解, 仅供参考 ) 基于系统设计时的向下兼容原理, 支持核心线程的平台, 也一定会支持用户线程 这时的操作系统就同时支持了用户线程和核心线程 ( 事实上我想目前大部分的 400 版本都应该是这种类型 ) 通常, 我们使用 M*N 的方式来表达这种类型 : 一个进程上运行着 M 个用户线程, 这 M 个用户线程共享该进程对应系统底层的 N 个核心线程 用户线程位于核心线程的上层, 可由我们用户控制的 ; 核心线程是由系统控制的, 对用户是不可见的 系统只对更加昂贵的核心线程分配资源 用户线程到核心线程的解析由系统来完成 线程的程序模型 400 支持 call-return 的程序模型 在其它的平台中, 如果程序 A 想用调用程序 B, 那么系统就必须再启动一个进程来运行这个程序 B, 或者是在当前进程中用程序 B 来代替程序 A 而 400 的进程管理机制, 决定了一个进程可以对应多个程序, 这多个程序共享相同的进程资源, 所以可以很轻易的实现 call --return 这种方式 这里稍微说明一下, 所谓 CALL return 模型, 也就是说程序 A 调用程序 B, 程序 B 的返回值, 也就是我们所说的接口数据, 可以在程序 A 中直接使用 举个常见的与之相反的例子, 在 C 语言中, 程序的调用就只有输入, 没有返回 ( 函数才有返回, 两个 main 函数之前的信息传递是单向的 )

8 启动另一个进程的需要耗费时间, 以及系统资源 为了避免这种消耗, 程序员们通常会使用动态链接库 (DLL) 每当程序需要使用到动态链接库中的服务时, 程序就载入动态链接库, 然后调用函数, 来实现程序所需要的服务 尽管 400 的 I5 平台中, 多线程程序支持 call return 的程序模型, 但是 IBM 公司还是强烈建议我们在调用的活动作业组中使用服务程序 (service program), 或动态链接态 这种建议主要是基于程序应用跨平台时的考虑 虽然不是必须, 但多线程编程所调用的程序最好是 ILE 环境下的程序, 而不是 OPM 程序 ( 也就是不建议使用 RPG, 尽量用 RPGLE) 因为 OPM 程序在多线程程序中, 需要注意到一些特殊的地方, 比如说线程的安全性 JOB 和 JOB 资源 前面已经提到过,JOB 包含了存储和其它资源 存储 (storage) 又包括了数据和堆栈数据 : 数据是指存放程序变量的地点 具体而言, 可分为三类 : 全局变量和静态变量 ( 简称 static) 动态分配的存储 ( 简称 heap) 本地函数变量 ( 简称 automatic) 程序变量的存储空间由活动作业组 (activation group) 分配, 运行中的程序信息都保存在活动作业组中 所有运行在同一个活动作业组中的线程都可以共享使用 static 和 heap; 而当前程序的变量, 以及 automatic 则由按当前线程分配使用, 线程之间不做共享 ( 也就是说各个线程原则上互不干扰 ) 堆栈 : 堆栈包含线程中调用的程序流或过程流的数据 (The stack contains data about the program or procedure call flow in a thread) 当建立一个线程的时候, 系统分配堆栈, 以及随机自动分配的存储空间 使用一个线程的时候, 堆栈以及随机自动分配的存储空间都被视为线程资源 当该线程结束时, 这些资源将会返回给进程, 由进程再分配给之后启动的其它线程去使用 一个活动作业组下的所有线程, 都共享这个活动作业组中的资源, 比如 static, heap( 当然, 这个活动作业组中的资源也是属于 JOB 的资源 ) 所以当一个线程更改了这类 JOB 资源时, 其它线程查询到的, 将会更改后的值 线程的私有数据和特有数据 有些数据资源, 虽然在程序中定义为全局变量, 但线程间不能共享, 而是由每个线程自已为这些数据资源分配存储空间 这类数据称之为线程的特有数据 thread-specific data 线程之间之间完全独立的数据, 称为线程的私有数据 thread private data. (Threads cannot share certain resources, but they can have their own view of data items called thread-specific data. Data that threads cannot share between themselves are called thread-private data.) 以下这几种资源都是属于线程的私有数据 :

9 线程标识符 ( 系统唯一 ) 线程优先级 ( 默认与 JOB 的相关 ) Security information 库列表 signal blocking mask Call stack Automatic storage 错误信息这个是指系统自带的 errno 有关线程的特有数据, 详见线程的特有数据 (thread specific data) 多线程编程的环境 交互式作业不支持多线程 ( 还有一种通讯类的作业也不支持多线程?), 所以要使用 测试多线程必须在子系统下, 比如用 SBMJOB 提交 同时, 提交到 JOB 时指定的 JOBD 要支持多线程, 附带再说明一下, 使用 SBMJOB 命令时, 如果不指定 JOBD, 那么就默认使用当前 USER 的 JOBD 可用 WRKJOBD 查看使用的 JOBD 的 Allow Multithread (ALWMLTTHD) 这个参数 ( 排位比较靠后 ), 该参数值为 YES 时表示该 JOBD 支持多线程, 为 NO 时表示不支持多线程 该值可用 CHGJOBD 命令来更改, 或在创建 JOBD 时指定 1.4 活动作业组 (Activation group) 基本资料 一个 JOB 下, 容纳活动中的程序 服务程序的子结构被称为活动作业组 (activation group), 活动作业组包含了运行程序所必须的资源, 这些资源是指 : static, heap 以及管理临时数据的资源 (static, heap 是简称, 具体解释详见上文的 JOB 和 JOB 资源 ); 活动作业组的作用范围与程序编译时的参数相关, 一个 JOB 就有可能对应多个活动作业组 ( 详见下面的注意事项 ) 一个 JOB 如果支持多线程编程的话, 那么这个 JOB 下的多个线程将可以共享这个 JOB 下的同一个活动作业组中的资源 ; 而一个线程可以运行不同的活动作业组中被激活的程序 系统不会保存线程与活动作业组之间对应关系表, 也就是说我们不知道一个活动作业组中运行了哪些线程, 也不知道一个线程对应哪些活动作业组 所以当一个活动作业组中还有线程在运行就将其关闭的话, 系统无法进行检测, 只能按照指令强制关闭该活动作业组, 此时就可能会产生无法预见的错误, 比如说进程非正常中断 为了避免这种情况, 在辅线程中所有关闭活动作业组的操作, 都会导致系统采用有序的方式 end 掉 JOB( 也就是说这种情况发生时, 系统为了避免非正常中断, 就主动正常中断 )

10 所谓的有序的中断, 也就是系统在 JOB 中 End 掉所有线程, 然后在初始线程中调用 exit 事务, 最后关闭掉所有文件 注意事项 程序编译时, 如果 ACTGRP 参数是 *NEW 的话, 那么每次调用这个程序, 都会产生一个新的活动作业组 ; 当程序执行 RETURN 语句时, 系统将会关闭这个新的活动作业组 也就是说, 程序如果是这样编译的话, 在辅线程中程序的 Return 将会导致 JOB 的 End 进一步解释, 应用系统中, 如果程序编译时,ACTGRP 参数使用了 NEW ; 而在程序结尾又使用了 Return 语句的话, 那么辅线程中程序的结束将会导致整个 JOB( 含初始线程 ) 都 End 掉, 这往往与我们的预想有偏差, 所以在使用时要加以注意 不过我们通常在编译程序时都会直接使用系统的默认参数 (QILE), 一般也就不会出现这个错误 还有一种情况也会导致 Job 的 End, 不过我翻译不出来 : If an exception has not been handled by the time it has percolated to the control boundary and the control boundary is a program entry procedure (PEP or main entry point), the multithread-capable job is ended. 然后, 使用 RCLACTGRP 这个命令时 ( 看这个名字就知道, 是回收活动作业组资源的 ), 只允许初始线程使用, 如果是辅线程使用的话, 系统将会报一个 CPF180B 的错 编译时的参数 如果编译程序时,ACTGRP 参数采用系统默认参数 (QILE), 或者是 named 的参数, 那么程序 Return 后, 活动作业组仍会保留 默认参数 (QILE) 是指在启动 Job 时, 就产生活动作业组 ; 在 End Job 的时候, 销毁活动作业组 ; Named 参数是指在 Job 中, 首次调用程序时产生活动作业组 ; 当程序 Return 之后, 这个活动作业组将会处于一个 last-used 的状态, 但不会被删除掉 几点疑惑 不知道编译程序时,ACTGRP 的参数又不会影响到调用程序时的版本, 也就是如果不是每次 Return 就销毁掉活动作业组, 程序更新而又没有 end 掉 JOB 的时候, 这个 Job 有没有可能还是调用活动作业组所指向的原来旧版本的程序? 如果旧版本的程序在内存中未清除的话, 会不会有可能产生更新了程序但仍然执行的是旧版本的问题? 如果是 C 程序, 用 linkage 绑定 RPG 程序来调用的话, 会不会即使编译程序时使用了 *NEW 参数, 也还是会有更新程序执行旧版本的问题? 还有, 如果程序中没有 Return 语句的话, 那么是否程序结束活动作业组也不会销毁? RPG 程序在编译时没有这个参数, 是不是活动作业组的概念并不针对于 RPG 程序? 很多事情都可能有关联的

11 1.5 调用 C 程序的注意事项 C 程序的调用有一定的特殊性, 系统隐含了一种可能会导致提前结束线程, 或是整个 JOB 的情况 就应用级程序而言, 这种情况出现的频率较上文提到的情况更为常见, 所以特别在此提示 : 假设线程中最外层的程序 A, 调用了其它的程序 B C D; 如果 B C D 是 C 程序, 或它们自身又调用了 C 程序 ( 这里的调用 C 程序, 是指调用 main() 函数 ), 当这个被调用的 C 程序结束时, 系统会有一个隐含的 pthread_exit(), 或 exit(), 这个隐含的操作在当前 JOBD 支持多线程时, 将会结束当前的线程 ; 如果当前线程是初始线程, 那还将会结束当前的 JOB 如果当前 JOBD 不支持多线程, 则不会这个问题 如果 B C D 是 RPGLE 程序, 它们没有调用 C 程序, 只是调用了 C 函数, 具体来说, 就是通过将 C 程序编译为 MODULE,RPGLE 程序也编译为 MODULE, 然后在 RPGLE 程序中使用 CALLB 的方式来调用 C 函数, 最后使用 CRTPGM 来生成 PGM 在这种情况下,RPGLE 程序的结束 ( 并不是 C 函数的结束 ), 也将会有一个隐含的 pthread_exit() 或 exit(), 如果当前 JOBD 支持多线程, 会结束当前线程 ; 如果当前线程是初始线程, 会结束当前 JOB 如果 JOBD 不支持多线程, 不会存在上述问题 附带再说明一下 C 程序中 exit() 与 return() 的区别 : return 是返回上层调用,exit 是结束当前程序, 然后再返回上层调用 也就是 exit 有一个结束当前程序的动作 如果 JOBD 支持多线程, 那么 C 函数中的 exit 还将会导致线程结束, 无论调用 C 函数的程序是否位于最上层调用 如果当前线程为初始线程, 会结束当前 JOB 如果 JOBD 不支持多线程,C 函数中的 exit 只会导致当前程序结束 ( 不仅限于当前调用的 C 函数, 而是当前程序 ), 但仍会返回上层调用程序, 不会结束线程 当然, 如果当前程序已位于最上层调用, 那么还是会结束线程的 无论 JOBD 是否支持多线程, 如果是采用调用 C 函数而不是调用 C 程序的方式,return 的使用都只会结束当前 C 函数, 返回上层调用它的程序或 Module, 不会导致程序结束, 也不会导致线程结束 但是在多线程环境中, 当前调用了 C 函数的程序结束, 仍会导致线程的结束, 无论是否还有上层调用, 参见上文描述 也就是说, 如果使用多线程技术, 那么 : 1 C 程序的 main() 函数只允许在最外层使用, 不能被任何程序调用 ; 2 调用了 C 函数的 RPGLE 程序, 尽量位于当前线程的最外层, 或是调用它的程序在调用之后不再进行其它处理, 否则会造成应用级别的异常中断 ; 3 如非必要,C 函数中尽量使用 return, 不使用 exit 如果 JOBD 不支持多线程, 就不会有这些问题 ; 所以应用系统如果要从单线程转换成为多线程, 那么在 C 程序或 C 函数的调用上需要特别留意 1.6 多线程编程的通讯问题 i5 支持的唯一的一种能保障线程安全的通讯协议, 就是 socket 协议 ( 也就是说 SNA, ODBC 之类的跨平台数据交互都不能使用多线程编程?) 在 400 上使用 socket 需要考虑到以下两点 :

12 SOCKET API: 大部分 socket 接口都能保障线程安全, 但是绝大多数网络路由器不能使用静态存储空间 那么我们通讯时使用的函数可能需要加上 _r 的后缀, 比如说, 原 gethostbyaddr(), 就需要改为 gethostbyaddr_r() 这类带_r 后缀的程序, 与 UNIX 定义的是兼容的 所有带 _r 后缀的程序都存在于服务程序 QSOSRV2 中 AnyNet: 在多线程程序中,AnyNet 也可以支持线程安全, 但是未经测试 1.7 线程的数据库 数据相关处理 数据库操作 : 支持线程安全的数据库操作包括了 : 创建文件, 增加 MEMBER, 删除文件, 删除 MEMBER 我们可以使用 DSPCMD 命令来查看当前环境下的这些命令是否支持线程安全就用户层面来看, 线程与线程间的数据操作, 同样是记录锁 比如说线程 1 执行了一个读操作, 线程 2 如果也要对该记录执行操作时 (operation against the same open instance), 线程 2 将会等待线程 1 结束读操作 读出的结果放在 I/O 缓冲里 如果线程操作的是不同的记录 ( 我觉得对于普通用户而言,open instance 指的多半就是打开文件操作某条记录的信息 ), 那么就不需要串行 多线程的 JOB 不支持分布式的文件 (Distributed files), 因为这些文件不能保障线程的安全 ODP 的共享 : 支持多线程的的 JOB 允许共享打开文件, 但是并不总是共享 ODP(Open data path) 如果文件定义了 SHARE(*YES) 和 OPNSCOPE(*ACTGRPDFN), 那么一个线程所 create 的子线程, 如果是运行同一个活动作业组中, 就可以共享 ODP; 如果文件定义了 SHARE(*YES) 和 OPNSCOPE(*JOB), 那么由同一个线程所 create 的子线程都就可以共享 ODP; ( 那么如果我们通常定义的 SHARE 都是 NO, 所以实际上同一进程下的各个线程默认就不是共享 ODP 了?) OVRDBF: 只有初始线程能使用 OVRDBF, 辅线程使用 OVRDBF 会报错 只有 JOB 级的, 和活动作业组级的 OVRDBF 能作用于辅线程 (JOB level, activation group level) 调用级别的 OVRDBF 对辅线程无效 (Call level) 同样,DLTOVR 命令也只能在初始线程中使用 回收资源 : 回收资源的命令, 对于多线程来说都是不安全的, 因为系统不会跟踪 (track) 资源, 所以无法识别资源是否仍在被线程使用 所以 RCLRSRC RCLACTGRP 不能在辅线程中调用, 但可以在初始线程中调用 如果在辅线程中调用, 系统将会报错

13 2 线程的基本管理操作 2.1 线程的属性 可以在启动一个线程时设置线程的属性, 或在线程运行的时候更改这些属性 常见的线程属性 : 优先级系统分配的运行时间堆栈空间影响到线程可以调用的函数数量名字我们可以根据线程的名字, 来 DEBUG 或是 TRACK 这个运行中的线程线程组我们可以通过线程组, 来管理同一时间运行的多个线程 Detach state 这个状态标识了当线程结束时, 我们如何回收, 或保留这个线程使用过的资源任务计划线程在系统或在应用中是如何被安排 计划的 继承判断线程的属性是否继承 更改线程的属性, 可以使用系统 API 函数, 如 pthread_attr_setdetachstate() 函数就可以更改 detach state 这个属性, 详见 Pthread_attr_setdetachstate() 更改线程属性后, 再启动的线程就将具备更改后的属性, 见下例 : #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ void *thethread(void *parm) printf("entered the thread\n"); return NULL;

14 int main(int argc, char **argv) pthread_attr_t attr; pthread_t thread; int rc=0; printf("enter Testcase - %s\n", argv[0]); printf("create a default thread attributes object\n"); rc = pthread_attr_init(&attr); checkresults("pthread_attr_init()\n", rc); printf("set the detach state thread attribute\n"); rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); checkresults("pthread_attr_setdetachstate()\n", rc); printf("create a thread using the new attributes\n"); rc = pthread_create(&thread, &attr, thethread, NULL); checkresults("pthread_create()\n", rc); printf("destroy thread attributes object\n"); rc = pthread_attr_destroy(&attr); checkresults("pthread_attr_destroy()\n", rc); printf("join now fails because the detach state attribute was changed\n"); rc = pthread_join(thread, NULL); if (rc==0) printf("unexpected results from pthread_join()\n"); exit(1); sleep(2); printf("main completed\n"); return 0; 2.2 启动线程 当我们的应用程序创建了一个线程的时候, 系统将会对线程的属性 控制结构 和运行时间等内容进行初始化, 以保证线程安全的运行 当然, 启动一个线程的时候, 我们也需要在应用程序中对该线程可能使用到的的数据和输入输出参数进行初始化 启动一个线程后, 系统将会为这个线程分配一个唯一的线程标识号 线程标识号是一个整型变量, 我们可以通过这个线程标识号, 来对该线程进行 DEBUG,TRACE, 或其它类型

15 的管理操作 但是不能通过线程标识号直接操作或控制这个线程 大部分线程的 API 函数都会返回线程描述符, 我们可以通过返回的线程描述符对线程进行直接操作, 也可能通过一些同步机制等待线程结束处理 下面的例子中, 主程序启动了一个线程, 并向这个线程传递了两个参数, 一个是整型变量, 一个是 124 位长的字符型变量 参数做为一个全局变量来定义 启动的线程不仅打印了主程序传递过来的参数, 而且还使用了 pthread_getthreadid_np() 函数取出自身的线程标识号, 注意该函数的输出是一个 pthread_id_np_t 类型的结构 ( 其实该结构里面也就是 hi,lo 两个整型变量 ) 这里主要用到的, 就是 pthread_create() 这个函数, 函数说明详见 pthread_create() #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ typedef struct int threadparm1; char threadparm2[124]; threadparm_t; void *thethread(void *parm) pthread_id_np_t tid; threadparm_t *p = (threadparm_t *)parm; tid = pthread_getthreadid_np(); printf("thread ID %.8x, Parameters: %d is the answer to \"%s\"\n", tid.intid.lo, p->threadparm1, p->threadparm2); return NULL; int main(int argc, char **argv) pthread_t thread; int rc=0; threadparm_t *threadparm; printf("enter Testcase - %s\n", argv[0]);

16 threadparm = (threadparm_t *)malloc(sizeof(threadparm)); threadparm->threadparm1 = 42; strcpy(threadparm->threadparm2, "Life, the Universe and Everything"); printf("create/start a thread with parameters\n"); rc = pthread_create(&thread, NULL, thethread, threadparm); checkresults("pthread_create()\n", rc); printf("wait for the thread to complete\n"); rc = pthread_join(thread, NULL); checkresults("pthread_join()\n", rc); printf("main completed\n"); return 0; 2.3 结束线程 结束线程通常是由该线程自身发起的 当一个线程完成了所有的处理之后, 它将会有一个关闭自身的动作, 释放系统资源以便之后其它的线程使用这些资源 有些 API 函数要求应用程序在程序结束时, 明确地给出释放资源的语句 也有些线程的处理机制没有这样要求 ( 如 JAVA) 可以有多种方法去结束一个线程 最好的方法就是 return 到创建这个线程的程序中 因为有关线程的 API 函数 有些 API 函数也支持 exception 机制 这里所说的 Exception 机制, 是指当发生一个一个 exception 而且没有去处理它的时候, 线程将会结束 下面的例子中, 主要是在线程调用的函数中使用的 pthread_exit() 函数来结束掉辅线程, 以及初始线程中使用的 pthread_join() 接收辅线程中的返回 Pthread_exit 函数的说明详见 pthread_exit 结束线程 Pthread_join 函数的说明详见 pthread_join #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \

17 const int THREADFAIL = 1; const int THREADPASS = 0; void *thethread(void *parm) printf("thread: End with success\n"); pthread_exit( VOID(THREADPASS)); printf("thread: Did not expect to get here!\n"); return VOID(THREADFAIL); int main(int argc, char **argv) pthread_t thread; int rc=0; void *status; printf("enter Testcase - %s\n", argv[0]); printf("create/start a thread\n"); rc = pthread_create(&thread, NULL, thethread, NULL); checkresults("pthread_create()\n", rc); printf("wait for the thread to complete, and release its resources\n"); rc = pthread_join(thread, &status); checkresults("pthread_join()\n", rc); printf("check the thread status\n"); if ( INT(status)!= THREADPASS) printf("the thread failed\n"); printf("main completed\n"); return 0; 2.4 取消线程 取消线程通常不是由该程序自身发起的, 而是由其它的线程发起 取消线程的时候要注意, 如果我们应用程序中, 对于清除数据与解锁的处理机制不合理时, 将可能会破坏数据, 或造成应用程序死锁 在下面这个例子中, 子线程所调用的函数每隔一秒钟打印一行, 主程序在创建子线程 3

18 秒钟后, 发出取消该子线程的指令 主要使用的函数为 pthread_cancel(),api 函数说明见 pthread_cancel 取消线程 注意到使用了 pthread_cancel 之后, 仍然要使用 pthread_join 函数等待子线程结束 ; 如果子线程被成功取消, 那么 pthread_join 函数取到的状态将为 PTHREAD_CANCELED #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ void *thethread(void *parm) printf("thread: Entered\n"); while (1) printf("thread: Looping or long running request\n"); pthread_testcancel(); sleep(1); return NULL; int main(int argc, char **argv) pthread_t thread; int rc=0; void *status; printf("enter Testcase - %s\n", argv[0]); printf("create/start a thread\n"); rc = pthread_create(&thread, NULL, thethread, NULL); checkresults("pthread_create()\n", rc); printf("wait a bit until we 'realize' the thread needs to be canceled\n"); sleep(3); rc = pthread_cancel(thread); checkresults("pthread_cancel()\n", rc);

19 printf("wait for the thread to complete, and release its resources\n"); rc = pthread_join(thread, &status); checkresults("pthread_join()\n", rc); printf("thread status indicates it was canceled\n"); if (status!= PTHREAD_CANCELED) printf("unexpected thread status\n"); printf("main completed\n"); return 0; 2.5 挂起和重新运行线程 有时我们需要暂时停止线程的运行 当我们挂起一个线程时, 这个线程的状态, 以及线程属性 锁住的记录, 都将维持现状, 直至线程重新开始运行 (resume) 挂起线程时要小心, 因为这可能会导致应用程序死锁, 或超时 我们可以使用其它更安全的方式来解决大部分问题, 包括挂起线程 ( 比如说同步机制 ) 挂起线程后, 我们需要在应用程序中重新启用这个线程, 重新启用后, 线程将从挂起点继续开始运行 2.6 等待线程结束 当我们使用线程的时候, 知道线程何时结束是很重要的 等待线程执行一个操作, 或等待线程发生一个事件, 称之为同步机制 常见的等待, 就是等待至线程结束 当线程结束的时候, 应用程序将会被提示线程分配的工作已完成, 或线程运行失败 我们可以通过 API 函数中设置的参数来确认线程运行的成功与否 在大型的应用程序中, 等待一组线程结束可能是一种比较好的方式 比如说通过调用程序 在下面这个例子中, 主程序启动了多个子线程, 并等待这些子线程结束, 然后检查子线程的结束状态 在这个例子中, 等待一个线程结束使用到的函数仍然是 pthread_join. #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define THREADGROUPSIZE 5

20 #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ void *thethread(void *parm) printf("thread %.8x %.8x: Entered\n", pthread_getthreadid_np()); printf("thread %.8x %.8x: Working\n", pthread_getthreadid_np()); sleep(15); printf("thread %.8x %.8x: Done with work\n", pthread_getthreadid_np()); return NULL; int main(int argc, char **argv) pthread_t thread[threadgroupsize]; void *status[threadgroupsize]; int i; int rc=0; printf("enter Testcase - %s\n", argv[0]); printf("create/start some worker threads\n"); for (i=0; i <THREADGROUPSIZE; ++i) rc = pthread_create(&thread[i], NULL, thethread, NULL); checkresults("pthread_create()\n", rc); printf("wait for worker threads to complete, release their resources\n"); for (i=0; i <THREADGROUPSIZE; ++i) rc = pthread_join(thread[i], &status[i]); checkresults("pthread_join()\n", rc); printf("check all thread's results\n"); for (i=0; i <THREADGROUPSIZE; ++i) if (status[i]!= NULL) printf("unexpected thread status\n");

21 printf("main completed\n"); return 0; 2.7 让进程先处理另一个线程 有时, 我们的应用程序需要使当前的线程让步于进程中其它线程 当线程发出一个让步指令 yielding 时, 系统将会立刻运行另一个相等或更高优先级的, 活动中的线程 如果当时准备运行的线程中, 没有优先极相等可更高的, 那么让步指令将不会产生效果 让步机制是实时处理的, 不会对之后的任务产生附加的影响 相关的 API 函数为 sched_yield. 目前 400 的 i 平台上, 提供了全面的多任务计划的算法 在一个写法良好的应用程序中, 线程很少需要采用让步, 因为系统提供了很多用来同步线程的 API 3 线程的安全性 (Thread safety) 一个函数可以被同一进程下的所有线程同时调用, 并且该函数内调用的函数也可以被所有线程同时调用时, 那么这个函数就具备线程安全性 (threadsafe) 根据测试, 绝大部分自已写的程序, 无论是 PGM, 还是 MODULE, 都不具备线程的安全性, 也就是不能被同一进程下的多个线程同时调用 3.1 存储用法和线程应用 当我们定义了变量之后, 多个线程就可能会访问或使用这些变量 应用程序中常用到的这些变量 ( 的存储空间 ), 以及其作用范围如下 : Global storage 全局变量 ( 的存储空间 ): 在一个源文件或 Module 中定义的全局变量, 对该应用程序中其它的源文件 Module 都是可见的 共享范围为整个应用程序 这种共享是一个很常见的线程安全问题 ( 也就是在多线程并发时, 要特别注意这类变量的赋值 ) Static storage 静态变量 ( 的存储空间 ) 静态变量也是全局变量的一种, 只不过它的作用范围被限制在声明该变量的源文件 Module 或函数中 共享范围为声明静态变量的可执行 Module 中 Heap storage 动态分配的存储空间这类存储由我们的应用程序动态进行控制, 分配 回收存储空间, 比如说 C 语言里的 malloc(), free() 函数 共享范围为整个应用程序 如果在动态分配了存储空间之后, 我们向另一个线程传递了该存储空间的指针 ( 比如通过全局变量, 或静态变量来传递, 或者是其它的方法传递 ), 那么另一个线程就可以通过这个指针, 来使用或回收这个存储空间 这种共享处理也是一个很常见的线程安全问题

22 Automatic storage 自动分配的存储空间函数内部私有变量, 由系统自动分配存储空间 自动分配的存储空间对该进程下的其它线程是不可见的 每次线程调用函数的时候, 都会重新自动分配存储空间 每个线程都拥有它们自己的自动分配的存储空间 基于复杂的串行 同步机制, 一个线程不能访问另一个线程的自动分配的存储空间 操作系统在活动作业组中, 进一步限制了全局变量 静态变量 Heap 存储的作业范围 这意味着在不同的活动作业组中, 使用了相同的全局变量 静态变量的程序, 实际上访问的是这些变量和存储空间的不同版本?(This means that application code or threads that are running in different activation groups but are using the same global or static variables access different versions of those variables and their storage) 与此相似, 尽管一个活动作业组可以使用另一个活动作业组中分配的 Heap 存储空间, 但是不能回收 Heap 存储空间 ( 也就是使用的其实是不同的版本?) 3.2 JOB 级的资源 在编写多进程程序的时候, 我们需要考虑 JOB 级的资源, 当线程使用到这些资源的时候, 必须不能与该进程下其它线程冲突 有些资源的作业范围是活动作业组的级别, 比如说打开数据库文件 我们编写多进程程序时, 需要把这种活动作业组级别的资源也视为 JOB 级的资源来使用 如果一个线程在使用这些资源, 而另一个线程需要修改这些资源时, 我们的应用程序就需要考虑使用合适的同步机制来处理这类问题 常见的 JOB 级, 活动作业组级的资源有以下几种, 在使用时要注意线程间的冲突 : 动态分配的存储空间 静态变量 全局变量这是最常见的共享资源, 见上一节 打开的文件 (Open Files) 当我们打开了一个文件之后, 同一进程下的所有线程就可以共享文件系统的文件, 以及数据库文件, 这种共享可以通过线程之间传递指针或文件描述符来实现 工作目录总是进程级的共享 (Scope of process) Locales 地址? The locale of an application is an activation group resource. All threads share the locale. Changing the locale affects other threads with regard to collating sequences or other locale information. CCSID 环境变量 CCSID 与环境变量都是 JOB 级的资源 更改这两类资源, 将会影响到该 JOB 下的所有线程

23 3.3 API 的线程安全级别 每一个 API 函数都有一个线程安全级别 在使用这些 API 之前, 我们需要确认在多线程中调用的 API 是否足够安全 API 的线程安全级别分为以下几类 : 安全 (yes) 这类 API 函数可以在并发的多个线程中安全地同时调用, 而不需要进行任何限制 这种类型的 API 内部所调用的函数, 也具体线程安全性 一定条件下安全 (Conditional) 这个标志说明这类 API 函数所提供的功能, 有一些是不具体线程安全性的 在 API 函数的说明中, 会指出线程安全的限制条件 ( 即哪些条件下调用 API 函数, 线程是安全的 ) 这类 API 函数的产生有可能因为系统底层支持不具备线程安合性, 也可能是因为 API 函数会调用一个退出指针?(API can call a exit point) 举例而言, 许多文件系统的 API 函数在一个具体线程安全性的文件系统中使用文件, 是完全安全的 但是一些在一定条件下安全的 API 函数, 在相同的环境中, 就有可能拒绝访问 在 API 函数说明中, 将会说明在哪些条件下, 函数会拒绝访问 不安全 (No) 这类 API 函数不具备线程安全性, 本来不应该在多线程程序中使用 有时, 这类 API 函数可能会拒绝访问, 有时也不会 ( 大部分函数都不会拒绝访问 ) 与 CL 命令不同, 当调用不安全的 API 函数时, 系统不会在 JOB LOG 中产生调试信息 ( 也就是说 CL 命令调用这些不安全的 API 函数时, 会 JOBLOG 中生成调试信息 ) 在多线程中使用不具备线程安全性的 API 函数需要一定的技巧 3.4 CL 命令和线程安全 ILE 环境下的 CL 命令, 或是编译后的 CL 程序, 是具备线程安全性的 ; 原始程序模型 (OPM) 下的 CL 程序不具备线程安全性 OPM 环境下的 CL 代码, 或者是 4.3 以前的版本中 ILE 环境下的 CL 代码, 在 CL 命令执行时会发送一个 CPD000B 的调试信息, 接下来继续执行命令 ( 该执行的结果未知 ) 这可能会导致线程的不安全, 也可能不会, 取决于底层代码的支持 对于一个命令来说, 与线程安全相关的有两个参数 : 线程安全属性 (threadsafe attribute THDSAFE) 多线程作业运行属性 (multithreaded job action attribute MLTTHDACN) 多线程作业运行属性仅针对不具备线程安全性的命令 ( 即线程安全属性为 NO 时才有效 ), 系统对多线程作业运行属性设置不同参数时的处理如下 : *NORUN 系统先发一个 CPD000D 的调试信息, 然后不执行这个命令 在发送 CPD000D 之后, 将会再发送一个 CPF0001 的退出信息 *MSG

24 系统同样先发送一个 CPD000D 的调试信息, 然后开始执行这个命令 *RUN 系统不发送调试信息, 直接开始运行 如果一个 JOB 支持多线程, 但并没有使用多线程时, 系统也允许不具备线程安全性的程序直接运行 当我们使用 DSPCMD 命令查看该命令的多线程作业属性时, 有时系统显示的值为 *SYSVAL, 也就是系统默认值 这时可以使用 DSPSYSVAL 来做进一步查看 : DSPSYSVAL SYSVAL(QMLTTHDACN) 也可以用 CHGSYSVAL 来修改这个系统值 ( 不过一般开发人员好象没有权限更改系统值 ) 3.5 拒绝访问的函数和线程安全 基于系统完整性的考虑, 以及为防止数据的毁坏, 有些 API 函数以及 CMD 命令在一定条件下具备线程安全性, 而在某些条件下则不具备线程安全性 这些 API 以及命令将有可能会拒绝部分或所有的访问 拒绝访问的分类条件如下 : 多线程能力在这种情况下, 函数是否拒绝访问, 取决于当前 JOB 对多线程的支持能力 如果当前 JOB 支持多线程, 但并不关注当前 JOB 中线程的数量时, 那我们就不能调用这类函数 此时, 函数会返回一个 CPF1892 的退出信息给调用者 初始线程有些函数只能在初始线程中被调用 如果我们在辅线程中调用这类函数, 函数会返回一个 CPF180C 的的退出信息给调用者 如果要在辅线程中使用的话, 可以通过向初始线程发出一个请求, 然后初始线程调用的方式来实现这个功能 OVRDBF 就是一个常见的只能在初始线程中调用的例子 多个线程 (More than on thread) 在这种情况下,JOB 中的线程数量将会导致函数拒绝访问 如果 JOB 中超过了一个线程, 那么函数将会返一个 CPF180B 的退出信息给调用者 其它函数将会在返回错误信息时将其赋值为 ENOSAFE(3524). 这些在多线程下可能会拒绝访问的函数, 在整个 JOB 中只有一个线程运行时, 是不会拒绝访问的, 可以随时调用 所有访问文件系统的 API 函数, 都不具备线程安全性 3.6 退出点 (exit point) 不太理解这个 exit program 和 exit point 是什么概念, 没有用过 只知道在源代码处用

25 F13, 有个 user exit program 的参数, 可以写 RPG 程序, 通过三个指针变量取到当前编辑代码的信息, 不知道和这个有没有什么关系 但是这个是在交互式作业中使用的, 和多线程的联系似乎不大 With the i5/os registration facility, you can define exit points for functions in an application and register programs that run at those exit points. Some i5/os services also support the registration facility for registering exit programs. They have predefined exit points that are registered when those services are installed. The registration facility itself is threadsafe. You can use it to specify attributes of thread safety and multithreaded job actions for exit program entries. Without careful evaluation, however, you should not consider existing exit programs to be threadsafe, even though you can call exit programs in a multithreaded job. The same restrictions apply to exit programs as to any other code that runs in a multithreaded job. For example, only exit programs written using a threadsafe Integrated Language Environment (ILE) language that can be made threadsafe 4 多线程的程序技巧 在编写多进程的时候, 我们需要对当前的应用系统进行评估, 主要需要考虑线程安全性 在不知道调用的系统服务,API 是否具备线程安全性时, 必须假设它是不安全的 4.1 多线程的同步 当线程已具备安全性时, 多个线程之间的同步就变为最重要的部分 同步, 是指多个线 程之间互相配合, 以一定的关联方式使得操作得以连续不断地进行 (Synchronization is the cooperative act of two or more threads that ensures that each thread reaches a known point of operation in relationship to other threads before continuing) 没有使用同步机制去处理共享资 源, 是导致应用数据被破坏的最常见的原因 下面列出了常见的同步方式, 使用的系统资源由小到大排列 : 1. CMPSWP 指令 Compare and swap 2. 互斥体 Mutual exclusion(mutexes) and threads 3. 信号量 Semaphores and threads 4. 条件变量与线程 Condition variable and threads 5. 线程的同步单元 Threads as synchronization primitives 6. 空间锁 Space location lock 7. 目标锁 Object lock

26 4.1.1 CMPSWP 我们可以通过系统的 CMPSWP(compare and swap) 指令, 在多线程程序中访问数据 CMPSWP 指令的语法格式是 CMSWP(&operand1, &operand2, swap operand), 也就是前两位是变量地址, 第三位变量数值 ( 后面还有扩展参数, 先不做深究 ) 系统根据地址, 比较 operand1 与 operand2 的值 如果这两个值相等, 那么将 swap operand 的值赋到 operand2 中, 返回 1 如果这两个值不等, 那么将 operand2 的值将赋到 operand1 中, 返回 0 如果返回 1 时 ( 相等 ), 那么系统将会确保在取出 operand2 的值来比较和将 swap operand 的值赋到 operand2 这段时间之内, 没有别的 CMPSWP 指令访问 operand2 这也就保障了数据的安全性 如果返回不等时, 并不能保证 operand1 不被别的 CMPSWP 指令访问 因此只允许 operand2 做为并发控制中的共享变量 Operand1, operand2, swap operand 的长度必须相等, 允许长度范围在 位字节 (byte) 在下面这个例子中, 注意对 CMPSWP 命令的使用 该程序宏定义了一个 ATOMICADD 的函数原型, 该函数用来将共享变量 var 加上 val 该函数原型中,while 循环的条件, 对 CMPSWP 命令的返回取反判断, 也就是说 : aatemp1 与 var 的值相等时, 将 aatemp2 的值赋值到 var 中 ( 这一步是由 CMPSWP 命令自动完成 ), 退出循环 ; aatemp1 与 var 的值不等时, 每次都对 aatemp2 重新赋值, 使其等于当前的 aatemp1+val ( 因为进行完 CMPSWP 命令后, 当其时的 var 的值已赋到 aatemp1 中 ; 而且不能将 aatemp2 赋值为 var+val, 因为此时的 var 的值可能又被其它并发的线程所更改 ) 程序执行结束后, 结果应该为 如果在线程函数 thethread 中, 没有使用 ATOMICADD 这个函数, 而是直接使用 sharedata++; 这样的语句, 那么执行出来的结果将不会是 , 因为程序中没有对 sharedata 变量进行保护, 当多个线程同时去更改该变量时, 实际上只有最后一个更改的生效 CMPSWP 命令是占用系统资源最小的一种同步机制 例 : #include <mih/cmpswp.h> #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define ATOMICADD(var, val) \ int aatemp1 = (var); \ int aatemp2 = aatemp1 + val; \ while(! _CMPSWP( &aatemp1, &var, aatemp2 ) ) \ aatemp2 = aatemp2 + val; \

27 #define NUMTHREADS 10 #define LOOPCONSTANT int sharedata=0; void *thethread(void *parm) int loop; printf("thread %.8x, %.8x is Entered\n", pthread_getthreadid_np()); for(loop=0;loop<loopconstant;loop++) ATOMICADD(shareData, 1); return NULL; int main(int argc, char **argv) pthread_t thread[numthreads]; int rc=0; int i; for (i=0; i<numthreads; i++) rc = pthread_create(&thread[i], NULL, thethread, NULL); for (i=0; i <NUMTHREADS;i++) rc = pthread_join(thread[i], NULL); printf("data = %d\n", sharedata); return 0; 互斥体 互斥体 mutex 是 Mutual exclusion 的简称 我们可以通过对互斥体的处理, 来实现多线程并发时, 一个时点上只允许一个线程处理数据的目的 互斥体的常见操作是 create, lock, unlock,destory 一个线程成功的对互斥体执行了 lock 操作后, 这个线程就成为了该互斥体的拥有者, 直到该线程对互斥体执行 unlock 操作 当执行了 unlock 操作后, 系统就会将互斥体交由另一个排队等待 lock 该互斥体的线程中 一个互斥体只能有一个拥有者 互斥体操作可以递归 递归的互斥体允许拥有者重复地 lock 互斥体 互斥体的拥者将会保持当前状态直到 unlock 的请求次数与 lock 的请求次数相同 互斥体可以设置超时等待, 也可以设置为立刻返回 互斥体是我们在进行多线程编程是常用的一种方式 更具体信息以及使用方法, 可查看操作互斥体的 API 互斥体操作 API -- Mutex Operation API.

28 4.1.3 信号量 信号量 ( 有时可以认为是信号量计数器 ) 可以用来控制对共享资源的访问 一个信号量, 实际上也是一个整型的计数器 每个信号量当前都有一个数值, 这个数值大于或等于 0 ( 有的系统可能允许小于零, 用来标识当前阻塞的线程数量, 按文中的表述, 在 400 上信号量的值不会小于零 ) 当线程 lock 信号量时, 信号量的值就会减 1; 如果信号量的值已为 0, 那么该线程将会阻塞, 直到另一个线程对信号量执行 unlock 操作 当线程 unlock 信号量时, 信号量的值会加 1, 然后唤起一个之前被阻塞的线程 可以认为信号量的初始值标识的其实是针对某类资源, 允许并发的线程数量 当初始值为 1 的时候, 一个 lock 操作就会将信号量的值减为 0; 之后其它的线程再进行 lock 操作时, 将会被阻塞 此时的信号量就与互斥体很类似了 不过与互斥体的不同点在于信号量没有所有权的概念, 也就是当信号量由 A 线程执行 lock 操作后, 可以由 B 线程去执行 unlock 操作 ; 而互斥体必须由当前 lock 的线程来执行 unlock 信号量这个特性可能会导致一些不可预测的结果, 需要注意 JAVA( 或者说 400 上的 JAVA 程序 ) 不能使用信号量 信号量的使用方法, 可查看操作信号量的 API 信号量的 API Condition variable and threads 条件变量的设置 (Condition variable, 简写作 CV) 允许线程等待一定的事件发生或一定条件满足时, 才开始继续运行 线程可以等待指定条件的发生, 同时另一些线程会将发生的条件事件广播出去以激活那些等待这些条件事件的线程 可以认为条件变量类似于别的平台上使用 event 去同步线程的机制 条件变量没有所有者, 也没有状态 (stateless) 没有状态, 也就意味着当一个线程发出一个信号, 标识某个事件发生, 如果当时没有符合这个事件的线程在等待, 那么这个信号将会被抛弃, 系统不再进行任何处理, 这个信号就丧失了有效性 进一步说, 条件变量的有效性是实时的, 如果线程 A 挂起, 等待某个事件发生而激活, 线程 B 则发出一个信号标识这个事件发生用来激活已挂起的线程 A 但如果线程 B 的处理在线程 A 挂起之前, 那么线程 A 将会一直挂起, 因为这个条件已经发生 具体信息详见条件变量的 API Threads as synchronization primitives 当一个指定线程等待另一个线程结束运行, 才开始继续运行时, 我们也可以认为这也是一种线程之间的同步 这种比较原始的同步机制没有所有者的概念, 它仅仅只是一个线程等待另一个线程结束而已 比如说 sched_yield 这个函数就可以使当前线程在另一线程结束之后运行, 详见 sched_yield

29 4.1.6 Space location lock 空间地址锁 (space location lock), 是一个存放在单字节空间中的逻辑锁 空间地址锁不会改变应用程序所使用到的存储空间, 它是系统自身所使用到的一个信息记录片 空间地址锁用起来, 与互斥体有点类似, 它与互斥体有几下几个方面的不同 : 1 我们可以直接使用空间地址锁来操作数据 空间地址锁不需要应用程序去创建和管理额个的目标 ( 相比之互斥体就需要创建一个互斥体变量, 使用完毕之后还需要销毁掉 ) 互斥体是作用于线程的, 而空间地址锁是直接作用于某个变量的 也就是说它只关注某个变量是否被锁, 在使用上与互斥体类似, 但在概念与互斥体有较大差异 2 Space location locks allow an application to coordinate the use of different locking request types. For example, more than one thread can use space location locks to acquire a shared lock on the same data.( 不同的线程可以使用空间地址锁获取同一个数据的共享锁?) 3 Due to the extra lock types that are provided by space location locks, the concept of an owner is slightly different than with mutexes. There can be multiple owners of a shared lock if each owner has successfully acquired the shared lock. For a thread to get an exclusive lock, all of the shared locks must be unlocked. 4 与互斥体的性能比较 空间地址锁锁住一个共享数据的路径, 大概需要 500 个 RISC 指令 (recude instrction set computer), 而互斥体只需要 50 个 ; 但是空间地址锁不需要创建 销毁等等指令 而互斥体大约需要 1000 个 RISC 指令 下面的例子, 说明了对空间地址锁的一个简单的用法, 主要用到了 locksl,unlosksl 这两个函数 注意空间地址锁直接对关键数据的操作 ( 锁 解锁 ) #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <mih/milckcom.h> /* Lock types */ #include <mih/locksl.h> /* LOCKSL instruction */ #include <mih/unlocksl.h> /* UNLOCKSL instruction */ #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ #define NUMTHREADS 3 int shareddata=0; int shareddata2=0;

30 void *thethread(void *parm) int rc; printf("thread %.8x %.8x: Entered\n", pthread_getthreadid_np()); locksl(&shareddata, _LENR_LOCK); /* Lock Exclusive, No Read */ /********** Critical Section *******************/ printf("thread %.8x %.8x: Start critical section, holding lock\n", pthread_getthreadid_np()); /* Access to shared data goes here */ ++shareddata; --shareddata2; printf("thread %.8x %.8x: End critical section, release lock\n", pthread_getthreadid_np()); unlocksl(&shareddata, _LENR_LOCK); /* Unlock Exclusive, No Read */ /********** Critical Section *******************/ return NULL; int main(int argc, char **argv) pthread_t thread[numthreads]; int rc=0; int i; printf("enter Testcase - %s\n", argv[0]); printf("hold Lock to prevent access to shared data\n"); locksl(&shareddata, _LENR_LOCK); /* Lock Exclusive, No Read */ printf("create/start threads\n"); for (i=0; i <NUMTHREADS; ++i) rc = pthread_create(&thread[i], NULL, thethread, NULL); checkresults("pthread_create()\n", rc); printf("wait a bit until we are 'done' with the shared data\n"); sleep(3); printf("unlock shared data\n"); unlocksl(&shareddata, _LENR_LOCK); /* Unlock Exclusive, No Read */ printf("wait for the threads to complete, and release their resources\n"); for (i=0; i <NUMTHREADS; ++i) rc = pthread_join(thread[i], NULL); checkresults("pthread_join()\n", rc);

31 printf("main completed\n"); return 0; Object lock 原文这里居然没有举例, 反正有了上面那么多同步的方法, 这里就略过算了 4.2 初始化和线程安全 有的时候, 我们希望延缓线程的初始化, 直到我们需要使用这些的资源的时候才进行初始化 然而对于多个线程而言, 需要系统一开始就为它们分配相应的资源, 这些资源必须具备线程安全性, 而且只能进行一次初始化 再具体一点, 比如说我们希望在子线程中, 只对某个全局变量进行一次初始化, 但子线程里的程序该如何写? 有多种方法来实际这个目的, 比如说可以一个布尔型变量, 让应用程序对这个变量进行判断来决定是否执行初始化 不过使用 pthread_once 这个函数就会显得比较专业一点 函数的具体使用方法详见 pthread_once 执行一次初始化 4.3 线程的特有数据 (thread specific data) 有时在应用程序中使用的全局变量, 我们希望它只作用于当前的线程 即我们希望每个子线程有针对它们自身的私有的全局变量, 这个全局变量对别的线程不可见, 这时就可以使用线程的特有数据 (Thread specific data) 这个全局变量由当前线程分配空间, 并加以保存 可以为这个全局变量分配一个销毁函数, 当当前线程结束时, 系统将会自动运行这个销毁函数, 来清空当前线程分配的存储空间 我们可以使用线程的特有数据来代替全局变量, 因为一个线程中, 所有的函数对这片存储空间的请求, 都会得到一个相同的值 而其它线程中的函数在相同的语句中访问的, 是调用函数的线程自身的存储空间 ( 也就是线程与线程之间互不干扰 ) 与线程的特有数据相关的函数为 : pthread_key_create() pthread_setspecific() pthread_getspecific() pthread_key_delete() 详见线程特有数据 API Thread specific storage API 在下面这个例子中, 定义了一个 pthread_key_t 结构的变量 tlskey, 这个变量虽然是全局变量, 但在这个程序的使用中, 实际上就是一个线程的特有数据, 即它的作用空间仅限于当前线程 首先, 需要使用 pthread_key_create 命令来创建生成这个线程特有数据, 同时为这个数据指定一个销毁函数 globaldestructor 当子线程结束时, 如果 tlskey 自身, 以及它所指向的数据都不为空的话, 那么系统将会自动运行销毁函数 globaldestructor 同时,

32 pthread_key_create 函数在销毁函数参数不为空的时候, 也就相当于将 tlskey 指定成为的销毁函数的入口参数, 所以在销毁函数中, 直接对参数进行 free() 操作, 其实也就是将 tlskey 指向的地址空间释放, 最后再把这片地址空间赋值为空 在主线程中, 每次创建子线程之前, 都先分配一片存储空间, 然后就这片空间的指针传递到子线程中 ; 子线程通过 pthread_setspecific 函数, 将线程特有数据 tlskey 指向了这片存储空间 于是, 在当前线程中, 所有的函数都可以共享这个特有数据 tlskey, 然后通过这个 tlskey 再共享它所指向的存储空间 比如说 showdata 函数, 就是通过 pthread_getspecific 函数使用了 tlskey, 从而访问到了存储空间中的数据 当每个子线程结束的时候, 系统将会自动运行销毁函数, 打印出一句话, 然后执行 free 操作, 最后再使用 pthread_setspecific 函数, 将线程特有数据 tlskey 指向的存储空间清空 #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ #define NUMTHREADS 3 pthread_key_t tlskey = 0; typedef struct int data1; int data2; mystruct; void globaldestructor(void *value) printf("%.8x,%.8x: In the data destructor\n", \ pthread_getthreadid_np()); free(value); pthread_setspecific(tlskey, NULL); void showdata() mystruct *gdata; gdata=pthread_getspecific(tlskey); printf("%.8x, %.8x: get data data1= %d, data2=%d\n", \ pthread_getthreadid_np(), gdata->data1, gdata->data2);

33 void *threadfunc(void *parm) mystruct *gdata; gdata=(mystruct *)parm; printf("%.8x, %.8x: set data data1= %d, data2=%d\n", \ pthread_getthreadid_np(), gdata->data1, gdata->data2); pthread_setspecific(tlskey, gdata); showdata(); printf("%.8x, %.8x: Ready exit thread, run function destruct\n", pthread_getthreadid_np()); return NULL; int main(int argc, char **argv) pthread_t thread[numthreads]; int rc=0; int i=0; mystruct *gdata; printf("enter Testcase - %s\n", argv[0]); printf("create a thread local storage key\n"); rc = pthread_key_create(&tlskey, globaldestructor); checkresults("pthread_key_create()\n", rc); /* The key can now be used from all threads */ for(i=0;i<numthreads;i++) gdata=(mystruct *)malloc(sizeof(mystruct)); gdata->data1=i; gdata->data2=(i+1)*i; pthread_create(&thread[i], NULL, threadfunc, gdata); for(i=0;i<numthreads;i++) pthread_join(thread[i], NULL); printf("delete a thread local storage key\n"); rc = pthread_key_delete(tlskey); checkresults("pthread_key_delete()\n", rc); /* The key and any remaining values are now gone. */ printf("main completed\n"); return 0;

34 4.4 调用不具备线程安全性的函数 有些时候, 应用程序必须去调用那些不具备线程安全性的函数, 也一些变通的方法, 可以安全的调用这些函数 举例来说, 假如有个程序调用和 API 函数 foo(), 因为我们已经知道这个函数 foo() 是不具备线程安全性的, 所以必须通过一种安全的方法来调用它 有两种常见的方法 : 通过互斥体的控制, 来调用这个函数 ; 不过这个方法仅仅只限于调用已知源码的函数, 因为我们只能通过互斥体来控制同一时点只有一个线程在调用这个不安全的函数, 而如果这个函数中又调用了其它不安全的函数时, 就必须要对其它不安全的函数进行控制, 这一点在不知道源码时, 是无法实现的 也因为这个原因, 所以我们不要试图在自己的程序中, 用自已的串行逻辑去控制不具备线程安全性的函数使其达到线程安全性 另起一个 JOB 来完成我们需要的调用有几种方法, 来完成这个目的 : 1 如果应用程序中使用到不具备安全性的 CL 命令, 那么可以使用 Qp0zSystem() 这个函数来调用这些命令, 这是一个类似于 C 语言里面 system() 的函数, 系统会启动一个启的进程来完成这个 CL 命令, 并在当前线程中等待 CL 命令执行结束并返回 (0 成功 ; 1 CL 命令不成功 ; -1--Qp0zSystem() 函数执行不成功 ) 例 : #include <stdio.h> #include <qp0z1170.h> int main(int argc, char *argv[]) if (Qp0zSystem("CRTLIB LIB(XYZ)")!= 0) printf("error creating library XYZ.\n"); else printf("library XYZ created.\n"); return(0); 2 如果应用程序中调用了不具备安全性的 API 或程序, 可以使用 spawn() 函数来启一个 JOB 去运行 Spawn() 函数可以继承原线程中的资源, 比如 IFS 文件, socket 描述符 Spawn 的例子可见 spawn 3 如果应用程序频繁的调用了不具备安全性的多个函数, 那么可以考虑通过上述方法, 启动一个新 JOB, 专门去运行这些函数 JOB 之间可以通过消息队列, 数据队列进行通讯

35 4.5 常见的多线程错误 在多线程编程中, 常见的错误有以下几种 : 调用不具备线程安全性的函数 这几乎是最常见的错误 在应用程序之中, 需要确保它调用的每一个 API 函数都具备线程的安全性 当前 JOB 不允许创建多线程要注意 JOBD 中 ALWMLTTHD 的值, 为 *YES 时才可以 交互式作业不支持多线程 如果当前 JOB 对应 JOBD 不支持多线程, 那么将无法运行多线程程序 关闭活动作业组进程下的一个活动作业组, 可能对应多个线程, 系统无法安全的关闭活动作业组, 所以当线程执行了关闭活动作业组的动作时 ( 比如说 C 程序中的 exit(),abort()), 系统将会结束掉整个进程 在前面的活动作业组, 以及调用 C 程序的注意事项中, 已就这个问题进行了应用层的表述 混合使用线程 API IBM 要我们不要把 pthread 的 API 和系统提供的其它线程管理的 API 混用, 比如说 JAVA 事务处理事务处理是 JOB 级, 或活动作业组级的 因为我们无法知道, 也无法控制线程运行与活动作业组的对应关系, 于是事实上, 事务处理就不能针对单个线程了 如果同时有多个线程在进行数据库操作, 那一个一个线程的 commit 操作, 可能会导致另一个活动中的线程也执行了 commit 操作 于是多线程编程, 在实际上就不支持事务处理 5 多线程 JOB 的 DEBUG 居然没有调试成功, 失败, 略 6 多线程 JOB 的性能 网上有很多这方面的资料, 所以略

36 6.1 多线程服务的建议 6.2 JOB 和线程的优先级 6.3 线程之间的冲突 6.4 线程应用中, 存储池大小设置的影响 6.5 存储池的活动级别 6.6 线程应用的性能 7 线程管理 API Thread management API 7.1 取线程属性 API -- Get Thread Attribute API pthread_attr_getdetachstate 取 detach 状态 pthread_attr_getinheritsched pthread_attr_getschedparam 取线程属性计 划参数 7.2 设置线程属性 API -- Set Thread Attribute API pthread_attr_init 初始化线程属性 pthread_attr_destroy 销毁线程属性

37 7.2.3 pthread_attr_setdetachstate() 设置线程属性 语法 : #include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 功能 : 设置线程属性中的 detach 状态 (detach state), 这个状态标识了当一个线程结束时, 系统是否会释放线程的资源 这里, 线程结束 这个用语, 包括但不仅限于线程的正常退出 ( 即也包括异常中断 ) 另外, 有部分资源 ( 如 automatic storage, 具体含义在 JOB 和 JOB 资源中 ), 是当线程结束时总是会被释放的 Detach 状态的值必须在下面两个中选择其一 : PTHREAD_CREATE_DETACHED // 就是 0, 表示释放资源? PTHREAD_CREATE_JOINABLE // 就是 1, 表示不释放资源? 系统的默认状态值是 PTHREAD_CREATE_JOINABLE 参数 : attr ( 输入参数 ) 标识线程属性结构的地址 detachstate ( 输入参数 ) 标识修改 detach state 状态的值, 必须为 PTHREAD_CREATE_DETACHED 或 PTHREAD_CREATE_JOINABLE 返回 : 0 表示成功非 0 表示失败 简单举例 : #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> int main(int argc, char **argv) int rc=0; int detachstate; pthread_attr_t pta; rc = pthread_attr_init(&pta); rc = pthread_attr_getdetachstate(&pta, &detachstate); printf("detach state = %d\n", detachstate); rc = pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_DETACHED); rc = pthread_attr_getdetachstate(&pta, &detachstate);

38 printf("detach state = %d\n", detachstate); return 0; 这个例子就是先对线程属性进行初始化, 然后更改 detach state 为了标识出更改前后的变化, 还使用到了 pthread_attr_getdetachstate 这个函数取出当前 detach state, 以示区分

39 7.2.4 pthread_attr_setinheritsched pthread_attr_setschedparam 7.3 取线程内容 API -- Get Thread Content API pthread_getconcurrency 取线程的并发等级 pthread_getpthreadoption_np pthread_getschedparam pthread_getthreadid_np 取当前线程的唯一 标识号 pthread_getunique_np 取指定线程的标识号 pthread_self 取当前运行线程的线程描述 符 7.4 设置线程内容 API -- Set Thread Content API pthread_setconcurrency 设置进程并发等级 pthread_setpthreadoption_np pthread_setschedparam

40 7.5 检查线程 API -- Check Thread API pthread_equal pthread_is_initialthread_np 检查当前线程是 否为初始线程 pthread_is_multithreaded_np 检查当前进程 是否拥有超过一个线程 7.6 线程管理 API pthread_clear_exit_np 清除线程的 EXIT 状 态 pthread_delay_np 线程 DELAY pthread_detach pthread_once 执行一次初始化 语法 : #include <pthread.h> int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); 功能 : 这个函数针对指定的变量 once_control, 只执行一次初始化 当多个线程先后执行了同样的 pthread_once 语句时, 初始化函数 init_routine 只会执行一次 初始化函数 init_routine 必须进行如下定义 : void initroutine(void); 参数 :

41 once_control 输入参数, 分配给初始化事件的控制变量 如果有不同初始化事件 ( 即需要调用不同的初始化函数 ), 那么需要定义不同的控制变量 该变量是一个 pthread_once_t 类型的结构体 init_route 输入参数, 初始化函数的指针, 这个函数没有入口参数, 也没有返回. 返回 : 0 成功 ; 非 0 -- 失败例子 : 下面这个例子, 就充分体现了 pthread_once 的用法 初始线程创建了三个子线程, 这三个子线程都使用了 pthread_once 语句, 但 pthread_once 语句中使用的初始化函数 initroutine 就只运行了一次 ( 即只会打印一次 In the initrouinte ), 而且 number 的值也只为 1 #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ #define NUMTHREADS 3 int number = 0; int okstatus = 777; pthread_once_t oncecontrol = PTHREAD_ONCE_INIT; void initroutine(void) printf("in the initroutine\n"); number++; void *threadfunc(void *parm) printf("inside secondary thread\n"); pthread_once(&oncecontrol, initroutine); return VOID(okStatus); int main(int argc, char **argv) pthread_t thread[numthreads];

42 int int void rc=0; i=numthreads; *status; printf("enter Testcase - %s\n", argv[0]); for (i=0; i < NUMTHREADS; ++i) printf("create thread %d\n", i); rc = pthread_create(&thread[i], NULL, threadfunc, NULL); checkresults("pthread_create()\n", rc); for (i=0; i < NUMTHREADS; ++i) printf("wait for thread %d\n", i); rc = pthread_join(thread[i], &status); checkresults("pthread_join()\n", rc); if ( INT(status)!= okstatus) printf("secondary thread failed\n"); exit(1); if (number!= 1) printf("an incorrect number of 1 one-time init routine was called!\n"); exit(1); printf("one-time init routine called exactly once\n"); printf("main completed\n"); return 0; pthread_trace_init_np PTHREAD_TRACE_NP sched_yield 语法 : #include <sched.h> int sched_yield(void);

43 功能 : 这个函数可以使用另一个级别等于或高于当前线程的线程先运行 如果没有符合条件的线程, 那么这个函数将会立刻返回然后继续执行当前线程的程序 参数 : 无返回 : 0 成功 ; 非 0 失败例子 : 下面这个例子中, 只是使用了 sched_yield 这个函数, 其实就实际效果上, 并未体现出其真正的意义, 主要旨在体会用法 #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <errno.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ #define LOOPCONSTANT 1000 #define THREADS 3 pthread_mutex_t int mutex = PTHREAD_MUTEX_INITIALIZER; i,j,k,l; void *threadfunc(void *parm) int loop = 0; int localprocessingcompleted = 0; int numberoflocalprocessingbursts = 0; int processingcompletedthisburst = 0; int rc; printf("entered secondary thread\n"); for (loop=0; loop<loopconstant; ++loop) rc = pthread_mutex_lock(&mutex); checkresults("pthread_mutex_lock()\n", rc); /* Perform some not so important processing */ i++, j++, k++, l++; rc = pthread_mutex_unlock(&mutex); checkresults("pthread_mutex_unlock()\n", rc);

44 /* This work is not too important. Also, we just released a lock and would like to ensure that other threads get a chance in a more co-operative manner. This is an admittedly contrived example with no real purpose for doing the sched_yield(). */ sched_yield(); printf("finished secondary thread\n"); return NULL; int main(int argc, char **argv) pthread_t threadid[threads]; int rc=0; int loop=0; printf("enter Testcase - %s\n", argv[0]); rc = pthread_mutex_lock(&mutex); checkresults("pthread_mutex_lock()\n", rc); printf("creating %d threads\n", THREADS); for (loop=0; loop<threads; ++loop) rc = pthread_create(&threadid[loop], NULL, threadfunc, NULL); checkresults("pthread_create()\n", rc); sleep(1); rc = pthread_mutex_unlock(&mutex); checkresults("pthread_mutex_unlock()\n", rc); printf("wait for results\n"); for (loop=0; loop<threads; ++loop) rc = pthread_join(threadid[loop], NULL); checkresults("pthread_join()\n", rc); pthread_mutex_destroy(&mutex); printf("main completed\n"); return 0;

45 7.7 线程操作 API -- Operation Thread API pthread_create() 创建线程 语法 : #include <pthread.h> int pthread_create( pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); 功能 : 按指定的属性创建一个线程 ( 即设置线程属性之类的 API 函数应在创建线程之前就调用 ), 同时会在线程中运行指定的线程函数, 主程序与线程函数之间可以通过一个指针来传递参数 当 pthread_create() 成功结束时, 返回的线程描述符将会保存下来, 用来指向该线程 ( 以便程序的后续处理, 例如主程序就可以通过这个返回线程描述符对线程进行操作 ) 当线程函数正常返回时, 系统隐含地调用了函数 pthread_exit() 创建的线程有可能 ( 但并不一定 ) 在 pthread_create 函数返回前就开始运行了 如果线程属性的值被更改的话, 之前已创建的线程并不受影响 如果不特别指定线程属性的话将会使用默认的线程属性 也就是说, 假如我们在程序段中已进行如下定义 : pthread_t t; void *foo(void *); pthread_attr_t attr; pthread_attr_init(&pta); 如果中间不做其它的处理, 那么下面这两句话是等价的 : pthread_create(&t, NULL, foo, NULL); pthread_create(&t, &attr, foo, NULL); 新创建的线程中,cancellation state 的值是 PTHREAD_CANCEL_ENABLE Cancellation type 的值是 PTHREAD_CANCEL_DEFERRED 初始线程是特殊的, 任何初始线程的结束 ( 如使用 pthread_exit(), 或其它结束初始线程的操作 ), 都会导致整个进程的结束 也就是说, 如果初始线程启动了子线程, 如果不做任何其它操作就结束初始线程的话, 那么所有的子线程都会立刻结束 系统并没有限制一个进程中可以启动的线程最大数 在实际使用中, 线程数量的限制决定于 JOB 中可用的存储空间

46 在创建线程之后, 最后总是使用 pthread_join() 或 pthread_detach() 函数, 使用这两个函数将可以使得线程结束时, 资源被回收 参数 : thread ( 输出参数 ) 创建的线程的描述符, 可以通过该描述符来操作这个建立的线程 attr ( 输入参数 ) 表明创建线程的属性, 如果使用 NULL, 则表示使用默认的线程属性 Start_routine 输入参数创建的线程中调用的函数 arg 输入参数主线程与创建的子线程之间传递参数的地址返回 : 0 表示成功非 0 表示失败 例子 : 在下面这个例子中, 主程序创建了两个线程, 这两个线程均调用函数 threadfunc(); 第一次创建的方式, 是使用 NULL 方式指定使用默认的线程属性 ; 第二次创建, 是通过一个 pthread_attr_t 结构的变量 pta, 来指定使用线程属性 因为主程序中进行了线程属性初始化之后, 没有再更改线程属性, 所以这两种创建方式实质上都是使用了默认的线程属性 创建了两个线程之后, 再将线程属性的目标 destory 根据上文所说, 主程序中线程属性的变更不会影响到已创建的线程 所以这里的 destory 对刚才已创建的线程没有影响 主程序与线程之间可以通过一个指针来传递参数, 本程序中指针对应的参数设为一个结构体变量, 结构体中含有一个整型变量, 和一个 128 位长的字符变量 我们自己写的程序可以参照这种方式来传递多个变量 程序打印出来的结果, 有可能是 : Create a thread attributes object Create thread using the NULL attributes Create thread using the default attributes Destroy thread attributes object Inside secondary thread, parm = 5 Inside secondary thread, parm = 77 Main completed 或 Create a thread attributes object Create thread using the NULL attributes Create thread using the default attributes Inside secondary thread, parm = 5 Destroy thread attributes object Inside secondary thread, parm = 77 Main completed 注意到 Destroy thread attributes object 这句话有可能在第二个线程运行之前开始执行,

47 也有可能在第一个线程运行之前就开始执行, 这是由系统去分配资源执行的, 我们无法控制 如果把 sleep(5) 这句话去掉的话, 那么线程执行函数 threadfunc() 中的打印语句, 本来应该打印两句, 就有可能一句都没有打印或是只打印一句出来 因为主程序 ( 初始线程 ) 的结束, 会导致整个进程的结束 当然, 使用 sleep 这种等待方式只是用于测试, 既不安全, 又没有效率 在实际应用中, 我们会使用其它更有效率, 更安全的语句来等待辅线程的结束, 比如 pthread_join(). #define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define checkresults(string, val) \ if (val) \ printf("failed with %d at %s", val, string); \ exit(1); \ \ typedef struct int value; char string[128]; thread_parm_t; void *threadfunc(void *parm) thread_parm_t *p = (thread_parm_t *)parm; printf("%s, parm = %d\n", p->string, p->value); free(p); return NULL; int main(int argc, char **argv) pthread_t thread; int rc=0; pthread_attr_t pta; thread_parm_t *parm=null; /************************************************************/ /* 线程属性初始化 */ printf("create a thread attributes object\n"); rc = pthread_attr_init(&pta);

提纲 1 2 OS Examples for 3

提纲 1 2 OS Examples for 3 第 4 章 Threads2( 线程 2) 中国科学技术大学计算机学院 October 28, 2009 提纲 1 2 OS Examples for 3 Outline 1 2 OS Examples for 3 Windows XP Threads I An Windows XP application runs as a seperate process, and each process may

More information

2005 Sun Microsystems, Inc Network Circle, Santa Clara, CA U.S.A. Sun Sun Berkeley BSD UNIX X/Open Company, Ltd. / Sun Sun Microsystems Su

2005 Sun Microsystems, Inc Network Circle, Santa Clara, CA U.S.A. Sun Sun Berkeley BSD UNIX X/Open Company, Ltd. / Sun Sun Microsystems Su Sun Microsystems, Inc. 4150 Network Circle Santa Clara, CA 95054 U.S.A. 819 7051 10 2006 10 2005 Sun Microsystems, Inc. 4150 Network Circle, Santa Clara, CA 95054 U.S.A. Sun Sun Berkeley BSD UNIX X/Open

More information

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc References (Section 5.2) Hsuan-Tien Lin Deptartment of CSIE, NTU OOP Class, March 15-16, 2010 H.-T. Lin (NTU CSIE) References OOP 03/15-16/2010 0 / 22 Fun Time (1) What happens in memory? 1 i n t i ; 2

More information

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

2 2 3 DLight CPU I/O DLight Oracle Solaris (DTrace) C/C++ Solaris DLight DTrace DLight DLight DLight C C++ Fortran CPU I/O DLight AM

2 2 3 DLight CPU I/O DLight Oracle Solaris (DTrace) C/C++ Solaris DLight DTrace DLight DLight DLight C C++ Fortran CPU I/O DLight AM Oracle Solaris Studio 12.2 DLight 2010 9 2 2 3 DLight 3 3 6 13 CPU 16 18 21 I/O DLight Oracle Solaris (DTrace) C/C++ Solaris DLight DTrace DLight DLight DLight C C++ Fortran CPU I/O DLight AMP Apache MySQL

More information

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.06.doc

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.06.doc 2 5 8 11 0 13 1. 13 2. 15 3. 18 1 23 1. 23 2. 26 3. 28 2 36 1. 36 2. 39 3. 42 4. 44 5. 49 6. 51 3 57 1. 57 2. 60 3. 64 4. 66 5. 70 6. 75 7. 83 8. 85 9. 88 10. 98 11. 103 12. 108 13. 112 4 115 1. 115 2.

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

ebook

ebook 3 3 3.1 3.1.1 ( ) 90 3 1966 B e r n s t e i n P ( i ) R ( i ) W ( i P ( i P ( j ) 1) R( i) W( j)=φ 2) W( i) R( j)=φ 3) W( i) W( j)=φ 3.1.2 ( p r o c e s s ) 91 Wi n d o w s Process Control Bl o c k P C

More information

mvc

mvc Build an application Tutor : Michael Pan Application Source codes - - Frameworks Xib files - - Resources - ( ) info.plist - UIKit Framework UIApplication Event status bar, icon... delegation [UIApplication

More information

untitled

untitled LBS Research and Application of Location Information Management Technology in LBS TP319 10290 UDC LBS Research and Application of Location Information Management Technology in LBS , LBS PDA LBS

More information

2/80 2

2/80 2 2/80 2 3/80 3 DSP2400 is a high performance Digital Signal Processor (DSP) designed and developed by author s laboratory. It is designed for multimedia and wireless application. To develop application

More information

C++ 程式設計

C++ 程式設計 C C 料, 數, - 列 串 理 列 main 數串列 什 pointer) 數, 數, 數 數 省 不 不, 數 (1) 數, 不 數 * 料 * 數 int *int_ptr; char *ch_ptr; float *float_ptr; double *double_ptr; 數 (2) int i=3; int *ptr; ptr=&i; 1000 1012 ptr 數, 數 1004

More information

chap07.key

chap07.key #include void two(); void three(); int main() printf("i'm in main.\n"); two(); return 0; void two() printf("i'm in two.\n"); three(); void three() printf("i'm in three.\n"); void, int 标识符逗号分隔,

More information

Important Notice SUNPLUS TECHNOLOGY CO. reserves the right to change this documentation without prior notice. Information provided by SUNPLUS TECHNOLO

Important Notice SUNPLUS TECHNOLOGY CO. reserves the right to change this documentation without prior notice. Information provided by SUNPLUS TECHNOLO Car DVD New GUI IR Flow User Manual V0.1 Jan 25, 2008 19, Innovation First Road Science Park Hsin-Chu Taiwan 300 R.O.C. Tel: 886-3-578-6005 Fax: 886-3-578-4418 Web: www.sunplus.com Important Notice SUNPLUS

More information

Multithreaded Programming Guide.ppt [兼容模式]

Multithreaded Programming Guide.ppt [兼容模式] 多线程编程 HUST Ke Shi Linux 系统下的多线程遵循 POSIX 标准线程接口 pthread 编写 Linux 下的多线程程序, 需要使用头文 件 pthread.h, 连接时需要使用库 libpthread.a 源文件 链接动态库 生成对象文件名 编译命令 :gcc **.c lpthread -o ** 1.2 一 POSIX 中常用函数 ( 一 ) 创建新线程 pthread_creat

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例 帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例 这篇文章主要介绍了帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例, 本文还详细介绍了帝国 CMS 数据库类中的一些常用方法, 需要的朋友可以参考下 例 1: 连接 MYSQL 数据库例子 (a.php)

More information

A Preliminary Implementation of Linux Kernel Virus and Process Hiding

A Preliminary Implementation of Linux Kernel Virus and Process Hiding 邵 俊 儒 翁 健 吉 妍 年 月 日 学 号 学 号 学 号 摘 要 结 合 课 堂 知 识 我 们 设 计 了 一 个 内 核 病 毒 该 病 毒 同 时 具 有 木 马 的 自 动 性 的 隐 蔽 性 和 蠕 虫 的 感 染 能 力 该 病 毒 获 得 权 限 后 会 自 动 将 自 身 加 入 内 核 模 块 中 劫 持 的 系 统 调 用 并 通 过 简 单 的 方 法 实 现 自 身 的

More information

國家圖書館典藏電子全文

國家圖書館典藏電子全文 i ii Abstract The most important task in human resource management is to encourage and help employees to develop their potential so that they can fully contribute to the organization s goals. The main

More information

EK-STM32F

EK-STM32F STMEVKIT-STM32F10xx8 软 件 开 发 入 门 指 南 目 录 1 EWARM 安 装... 1 1.1 第 一 步 : 在 线 注 册... 1 1.2 第 二 步 : 下 载 软 件... 2 1.3 第 三 步 : 安 装 EWARM... 3 2 基 于 STMEVKIT-STM32F10xx8 的 示 例 代 码 运 行... 6 2.1 GPIO Demo... 6 2.2

More information

第7章-并行计算.ppt

第7章-并行计算.ppt EFEP90 10CDMP3 CD t 0 t 0 To pull a bigger wagon, it is easier to add more oxen than to grow a gigantic ox 10t 0 t 0 n p Ts Tp if E(n, p) < 1 p, then T (n) < T (n, p) s p S(n,p) = p : f(x)=sin(cos(x))

More information

ch_code_infoaccess

ch_code_infoaccess 地 產 代 理 監 管 局 公 開 資 料 守 則 2014 年 5 月 目 錄 引 言 第 1 部 段 數 適 用 範 圍 1.1-1.2 監 管 局 部 門 1.1 紀 律 研 訊 1.2 提 供 資 料 1.3-1.6 按 慣 例 公 布 或 供 查 閱 的 資 料 1.3-1.4 應 要 求 提 供 的 資 料 1.5 法 定 義 務 及 限 制 1.6 程 序 1.7-1.19 公 開 資

More information

BC04 Module_antenna__ doc

BC04 Module_antenna__ doc http://www.infobluetooth.com TEL:+86-23-68798999 Fax: +86-23-68889515 Page 1 of 10 http://www.infobluetooth.com TEL:+86-23-68798999 Fax: +86-23-68889515 Page 2 of 10 http://www.infobluetooth.com TEL:+86-23-68798999

More information

<4D6963726F736F667420576F7264202D205F4230365FB942A5CEA668B443C5E9BB73A740B5D8A4E5B8C9A552B1D0A7F75FA6BFB1A4ACFC2E646F63>

<4D6963726F736F667420576F7264202D205F4230365FB942A5CEA668B443C5E9BB73A740B5D8A4E5B8C9A552B1D0A7F75FA6BFB1A4ACFC2E646F63> 運 用 多 媒 體 製 作 華 文 補 充 教 材 江 惜 美 銘 傳 大 學 應 用 中 文 系 chm248@gmail.com 摘 要 : 本 文 旨 在 探 究 如 何 運 用 多 媒 體, 結 合 文 字 聲 音 圖 畫, 製 作 華 文 補 充 教 材 當 我 們 在 進 行 華 文 教 學 時, 往 往 必 須 透 過 教 案 設 計, 並 製 作 補 充 教 材, 方 能 使 教 學

More information

CC213

CC213 : (Ken-Yi Lee), E-mail: feis.tw@gmail.com 49 [P.51] C/C++ [P.52] [P.53] [P.55] (int) [P.57] (float/double) [P.58] printf scanf [P.59] [P.61] ( / ) [P.62] (char) [P.65] : +-*/% [P.67] : = [P.68] : ,

More information

國立中山大學學位論文典藏.PDF

國立中山大學學位論文典藏.PDF 國 立 中 山 大 學 企 業 管 理 學 系 碩 士 論 文 以 系 統 動 力 學 建 構 美 食 餐 廳 異 國 麵 坊 之 管 理 飛 行 模 擬 器 研 究 生 : 簡 蓮 因 撰 指 導 教 授 : 楊 碩 英 博 士 中 華 民 國 九 十 七 年 七 月 致 謝 詞 寫 作 論 文 的 過 程 是 一 段 充 滿 艱 辛 與 淚 水 感 動 與 窩 心 的 歷 程, 感 謝 這 一

More information

untitled

untitled Co-integration and VECM Yi-Nung Yang CYCU, Taiwan May, 2012 不 列 1 Learning objectives Integrated variables Co-integration Vector Error correction model (VECM) Engle-Granger 2-step co-integration test Johansen

More information

K301Q-D VRT中英文说明书141009

K301Q-D VRT中英文说明书141009 THE INSTALLING INSTRUCTION FOR CONCEALED TANK Important instuction:.. Please confirm the structure and shape before installing the toilet bowl. Meanwhile measure the exact size H between outfall and infall

More information

W. Richard Stevens UNIX Sockets API echo Sockets TCP OOB IO C struct C/C++ UNIX fork() select(2)/poll(2)/epoll(4) IO IO CPU 100% libevent UNIX CPU IO

W. Richard Stevens UNIX Sockets API echo Sockets TCP OOB IO C struct C/C++ UNIX fork() select(2)/poll(2)/epoll(4) IO IO CPU 100% libevent UNIX CPU IO Linux muduo C++ (giantchen@gmail.com) 2012-09-30 C++ TCP C++ x86-64 Linux TCP one loop per thread Linux native muduo C++ IT 5 C++ muduo 2 C++ C++ Primer 4 W. Richard Stevens UNIX Sockets API echo Sockets

More information

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢   学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 Email: 51141201063@ecnu.cn 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Java 类型 引用 不可变类型 对象存储位置 作用域 OOP

More information

Windows XP

Windows XP Windows XP What is Windows XP Windows is an Operating System An Operating System is the program that controls the hardware of your computer, and gives you an interface that allows you and other programs

More information

概述

概述 OPC Version 1.6 build 0910 KOSRDK Knight OPC Server Rapid Development Toolkits Knight Workgroup, eehoo Technology 2002-9 OPC 1...4 2 API...5 2.1...5 2.2...5 2.2.1 KOS_Init...5 2.2.2 KOS_InitB...5 2.2.3

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

東吳大學

東吳大學 律 律 論 論 療 行 The Study on Medical Practice and Coercion 林 年 律 律 論 論 療 行 The Study on Medical Practice and Coercion 林 年 i 讀 臨 療 留 館 讀 臨 律 六 礪 讀 不 冷 療 臨 年 裡 歷 練 禮 更 老 林 了 更 臨 不 吝 麗 老 劉 老 論 諸 見 了 年 金 歷 了 年

More information

3.1 num = 3 ch = 'C' 2

3.1 num = 3 ch = 'C' 2 Java 1 3.1 num = 3 ch = 'C' 2 final 3.1 final : final final double PI=3.1415926; 3 3.2 4 int 3.2 (long int) (int) (short int) (byte) short sum; // sum 5 3.2 Java int long num=32967359818l; C:\java\app3_2.java:6:

More information

RunPC2_.doc

RunPC2_.doc PowerBuilder 8 (5) PowerBuilder Client/Server Jaguar Server Jaguar Server Connection Cache Thin Client Internet Connection Pooling EAServer Connection Cache Connection Cache Connection Cache Connection

More information

WTO

WTO 10384 200015128 UDC Exploration on Design of CIB s Human Resources System in the New Stage (MBA) 2004 2004 2 3 2004 3 2 0 0 4 2 WTO Abstract Abstract With the rapid development of the high and new technique

More information

1505.indd

1505.indd 上 海 市 孙 中 山 宋 庆 龄 文 物 管 理 委 员 会 上 海 宋 庆 龄 研 究 会 主 办 2015.05 总 第 148 期 图 片 新 闻 2015 年 9 月 22 日, 由 上 海 孙 中 山 故 居 纪 念 馆 台 湾 辅 仁 大 学 和 台 湾 图 书 馆 联 合 举 办 的 世 纪 姻 缘 纪 念 孙 中 山 先 生 逝 世 九 十 周 年 及 其 革 命 历 程 特 展

More information

epub83-1

epub83-1 C++Builder 1 C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r 1.1 1.1.1 1-1 1. 1-1 1 2. 1-1 2 A c c e s s P a r a d o x Visual FoxPro 3. / C / S 2 C + + B u i l d e r / C

More information

27 :OPC 45 [4] (Automation Interface Standard), (Costom Interface Standard), OPC 2,,, VB Delphi OPC, OPC C++, OPC OPC OPC, [1] 1 OPC 1.1 OPC OPC(OLE f

27 :OPC 45 [4] (Automation Interface Standard), (Costom Interface Standard), OPC 2,,, VB Delphi OPC, OPC C++, OPC OPC OPC, [1] 1 OPC 1.1 OPC OPC(OLE f 27 1 Vol.27 No.1 CEMENTED CARBIDE 2010 2 Feb.2010!"!!!!"!!!!"!" doi:10.3969/j.issn.1003-7292.2010.01.011 OPC 1 1 2 1 (1., 412008; 2., 518052), OPC, WinCC VB,,, OPC ; ;VB ;WinCC Application of OPC Technology

More information

TLLFDEC2013.indd

TLLFDEC2013.indd GOOD PEOPLE MANAGEMENT AWARD 2 學教卓越 行政長官卓越教學獎 2010 / 2011 本校重視學生全人發展 致力提供具專業的教學環 6. 通識科的閱讀課藉報章及時事影片與同學進行課堂討 境 營造純樸良好的校風 建立優良的班級文化 積極提 論 提升學生的批判思考及高階思維能力 並藉不同形 升教學效能 善用資源為學生提供分組教學及各種增潤課 程 並成功為學生創造多元化的成功學習經驗

More information

Microsoft Word - TIP006SCH Uni-edit Writing Tip - Presentperfecttenseandpasttenseinyourintroduction readytopublish

Microsoft Word - TIP006SCH Uni-edit Writing Tip - Presentperfecttenseandpasttenseinyourintroduction readytopublish 我 难 度 : 高 级 对 们 现 不 在 知 仍 道 有 听 影 过 响 多 少 那 次 么 : 研 英 究 过 文 论 去 写 文 时 作 的 表 技 引 示 巧 言 事 : 部 情 引 分 发 言 该 生 使 在 中 用 过 去, 而 现 在 完 成 时 仅 表 示 事 情 发 生 在 过 去, 并 的 哪 现 种 在 时 完 态 成 呢 时? 和 难 过 道 去 不 时 相 关? 是 所 有

More information

软件测试(TA07)第一学期考试

软件测试(TA07)第一学期考试 一 判 断 题 ( 每 题 1 分, 正 确 的, 错 误 的,20 道 ) 1. 软 件 测 试 按 照 测 试 过 程 分 类 为 黑 盒 白 盒 测 试 ( ) 2. 在 设 计 测 试 用 例 时, 应 包 括 合 理 的 输 入 条 件 和 不 合 理 的 输 入 条 件 ( ) 3. 集 成 测 试 计 划 在 需 求 分 析 阶 段 末 提 交 ( ) 4. 单 元 测 试 属 于 动

More information

考試學刊第10期-內文.indd

考試學刊第10期-內文.indd misconception 101 Misconceptions and Test-Questions of Earth Science in Senior High School Chun-Ping Weng College Entrance Examination Center Abstract Earth Science is a subject highly related to everyday

More information

\\Lhh\07-02\黑白\内页黑白1-16.p

\\Lhh\07-02\黑白\内页黑白1-16.p Abstract: Urban Grid Management Mode (UGMM) is born against the background of the fast development of digital city. It is a set of urban management ideas, tools, organizations and flow, which is on the

More information

穨control.PDF

穨control.PDF TCP congestion control yhmiu Outline Congestion control algorithms Purpose of RFC2581 Purpose of RFC2582 TCP SS-DR 1998 TCP Extensions RFC1072 1988 SACK RFC2018 1996 FACK 1996 Rate-Halving 1997 OldTahoe

More information

Logitech Wireless Combo MK45 English

Logitech Wireless Combo MK45 English Logitech Wireless Combo MK45 Setup Guide Logitech Wireless Combo MK45 English................................................................................... 7..........................................

More information

Microsoft Word - 24.doc

Microsoft Word - 24.doc 水 陸 畢 陳 晚 明 飲 食 風 尚 初 探 蕭 慧 媛 桃 園 創 新 技 術 學 院 觀 光 與 休 閒 事 業 管 理 系 摘 要 飲 食 是 人 類 維 持 與 發 展 生 命 的 基 礎 之 一, 飲 食 風 尚 會 隨 著 社 會 地 位 物 質 條 件 以 及 人 為 因 素 轉 移, 不 同 階 層 的 飲 食 方 式, 往 往 標 誌 著 他 們 的 社 會 身 分, 甚 至 反

More information

The Development of Color Constancy and Calibration System

The Development of Color Constancy and Calibration System The Development of Color Constancy and Calibration System The Development of Color Constancy and Calibration System LabVIEW CCD BMP ii Abstract The modern technologies develop more and more faster, and

More information

LH_Series_Rev2014.pdf

LH_Series_Rev2014.pdf REMINDERS Product information in this catalog is as of October 2013. All of the contents specified herein are subject to change without notice due to technical improvements, etc. Therefore, please check

More information

4. 每 组 学 生 将 写 有 习 语 和 含 义 的 两 组 卡 片 分 别 洗 牌, 将 顺 序 打 乱, 然 后 将 两 组 卡 片 反 面 朝 上 置 于 课 桌 上 5. 学 生 依 次 从 两 组 卡 片 中 各 抽 取 一 张, 展 示 给 小 组 成 员, 并 大 声 朗 读 卡

4. 每 组 学 生 将 写 有 习 语 和 含 义 的 两 组 卡 片 分 别 洗 牌, 将 顺 序 打 乱, 然 后 将 两 组 卡 片 反 面 朝 上 置 于 课 桌 上 5. 学 生 依 次 从 两 组 卡 片 中 各 抽 取 一 张, 展 示 给 小 组 成 员, 并 大 声 朗 读 卡 Tips of the Week 课 堂 上 的 英 语 习 语 教 学 ( 二 ) 2015-04-19 吴 倩 MarriottCHEI 大 家 好! 欢 迎 来 到 Tips of the Week! 这 周 我 想 和 老 师 们 分 享 另 外 两 个 课 堂 上 可 以 开 展 的 英 语 习 语 教 学 活 动 其 中 一 个 活 动 是 一 个 充 满 趣 味 的 游 戏, 另 外

More information

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 -> 目录 1 大概思路... 1 2 创建 WebAPI... 1 3 创建 CrossMainController 并编写... 1 4 Nuget 安装 microsoft.aspnet.webapi.cors... 4 5 跨域设置路由... 4 6 编写 Jquery EasyUI 界面... 5 7 运行效果... 7 8 总结... 7 1 1 大概思路 创建 WebAPI 创建 CrossMainController

More information

Oracle 4

Oracle 4 Oracle 4 01 04 Oracle 07 Oracle Oracle Instance Oracle Instance Oracle Instance Oracle Database Oracle Database Instance Parameter File Pfile Instance Instance Instance Instance Oracle Instance System

More information

中国人民大学商学院本科学年论文

中国人民大学商学院本科学年论文 RUC-BK-113-110204-11271374 2001 11271374 1 Nowadays, an enterprise could survive even without gaining any profit. However, once its operating cash flow stands, it is a threat to the enterprise. So, operating

More information

1 LINUX IDE Emacs gcc gdb Emacs + gcc + gdb IDE Emacs IDE C Emacs Emacs IDE ICE Integrated Computing Environment Emacs Unix Linux Emacs Emacs Emacs Un

1 LINUX IDE Emacs gcc gdb Emacs + gcc + gdb IDE Emacs IDE C Emacs Emacs IDE ICE Integrated Computing Environment Emacs Unix Linux Emacs Emacs Emacs Un Linux C July 27, 2016 Contents 1 Linux IDE 1 2 GCC 3 2.1 hello.c hello.exe........................... 5 2.2............................... 9 2.2.1 -Wall................................ 9 2.2.2 -E..................................

More information

Microsoft Word - 11.doc

Microsoft Word - 11.doc 除 錯 技 巧 您 將 於 本 章 學 到 以 下 各 項 : 如 何 在 Visual C++ 2010 的 除 錯 工 具 控 制 下 執 行 程 式? 如 何 逐 步 地 執 行 程 式 的 敘 述? 如 何 監 看 或 改 變 程 式 中 的 變 數 值? 如 何 監 看 程 式 中 計 算 式 的 值? 何 謂 Call Stack? 何 謂 診 斷 器 (assertion)? 如 何

More information

hks298cover&back

hks298cover&back 2957 6364 2377 3300 2302 1087 www.scout.org.hk scoutcraft@scout.org.hk 2675 0011 5,500 Service and Scouting Recently, I had an opportunity to learn more about current state of service in Hong Kong

More information

Chn 116 Neh.d.01.nis

Chn 116 Neh.d.01.nis 31 尼 希 米 书 尼 希 米 的 祷 告 以 下 是 哈 迦 利 亚 的 儿 子 尼 希 米 所 1 说 的 话 亚 达 薛 西 王 朝 二 十 年 基 斯 流 月 *, 我 住 在 京 城 书 珊 城 里 2 我 的 兄 弟 哈 拿 尼 和 其 他 一 些 人 从 犹 大 来 到 书 珊 城 我 向 他 们 打 听 那 些 劫 后 幸 存 的 犹 太 人 家 族 和 耶 路 撒 冷 的 情 形

More information

天 主 教 輔 仁 大 學 社 會 學 系 學 士 論 文 小 別 勝 新 婚? 久 別 要 離 婚? 影 響 遠 距 家 庭 婚 姻 感 情 因 素 之 探 討 Separate marital relations are getting better or getting worse? -Exp

天 主 教 輔 仁 大 學 社 會 學 系 學 士 論 文 小 別 勝 新 婚? 久 別 要 離 婚? 影 響 遠 距 家 庭 婚 姻 感 情 因 素 之 探 討 Separate marital relations are getting better or getting worse? -Exp 天 主 教 輔 仁 大 學 社 會 學 系 學 士 論 文 小 別 勝 新 婚? 久 別 要 離 婚? 影 響 遠 距 家 庭 婚 姻 感 情 因 素 之 探 討 Separate marital relations are getting better or getting worse? -Explore the impact of emotional factors couples do not

More information

untitled

untitled 2006 6 Geoframe Geoframe 4.0.3 Geoframe 1.2 1 Project Manager Project Management Create a new project Create a new project ( ) OK storage setting OK (Create charisma project extension) NO OK 2 Edit project

More information

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d =

More information

Preface This guide is intended to standardize the use of the WeChat brand and ensure the brand's integrity and consistency. The guide applies to all d

Preface This guide is intended to standardize the use of the WeChat brand and ensure the brand's integrity and consistency. The guide applies to all d WeChat Search Visual Identity Guidelines WEDESIGN 2018. 04 Preface This guide is intended to standardize the use of the WeChat brand and ensure the brand's integrity and consistency. The guide applies

More information

Chapter 9: Objects and Classes

Chapter 9: Objects and Classes Java application Java main applet Web applet Runnable Thread CPU Thread 1 Thread 2 Thread 3 CUP Thread 1 Thread 2 Thread 3 ,,. (new) Thread (runnable) start( ) CPU (running) run ( ) blocked CPU sleep(

More information

untitled

untitled A, 3+A printf( ABCDEF ) 3+ printf( ABCDEF ) 2.1 C++ main main main) * ( ) ( ) [ ].* ->* ()[] [][] ** *& char (f)(int); ( ) (f) (f) f (int) f int char f char f(int) (f) char (*f)(int); (*f) (int) (

More information

<4D6963726F736F667420576F7264202D2032303130C4EAC0EDB9A4C0E04142BCB6D4C4B6C1C5D0B6CFC0FDCCE2BEABD1A15F325F2E646F63>

<4D6963726F736F667420576F7264202D2032303130C4EAC0EDB9A4C0E04142BCB6D4C4B6C1C5D0B6CFC0FDCCE2BEABD1A15F325F2E646F63> 2010 年 理 工 类 AB 级 阅 读 判 断 例 题 精 选 (2) Computer mouse How does the mouse work? We have to start at the bottom, so think upside down for now. It all starts with mouse ball. As the mouse ball in the bottom

More information

1. 請 先 檢 查 包 裝 內 容 物 AC750 多 模 式 無 線 分 享 器 安 裝 指 南 安 裝 指 南 CD 光 碟 BR-6208AC 電 源 供 應 器 網 路 線 2. 將 設 備 接 上 電 源, 即 可 使 用 智 慧 型 無 線 裝 置 進 行 設 定 A. 接 上 電 源

1. 請 先 檢 查 包 裝 內 容 物 AC750 多 模 式 無 線 分 享 器 安 裝 指 南 安 裝 指 南 CD 光 碟 BR-6208AC 電 源 供 應 器 網 路 線 2. 將 設 備 接 上 電 源, 即 可 使 用 智 慧 型 無 線 裝 置 進 行 設 定 A. 接 上 電 源 1. 請 先 檢 查 包 裝 內 容 物 AC750 多 模 式 無 線 分 享 器 安 裝 指 南 安 裝 指 南 CD 光 碟 BR-6208AC 電 源 供 應 器 網 路 線 2. 將 設 備 接 上 電 源, 即 可 使 用 智 慧 型 無 線 裝 置 進 行 設 定 A. 接 上 電 源 B. 啟 用 智 慧 型 裝 置 的 無 線 Wi-Fi C. 選 擇 無 線 網 路 名 稱 "edimax.setup"

More information

科学计算的语言-FORTRAN95

科学计算的语言-FORTRAN95 科 学 计 算 的 语 言 -FORTRAN95 目 录 第 一 篇 闲 话 第 1 章 目 的 是 计 算 第 2 章 FORTRAN95 如 何 描 述 计 算 第 3 章 FORTRAN 的 编 译 系 统 第 二 篇 计 算 的 叙 述 第 4 章 FORTRAN95 语 言 的 形 貌 第 5 章 准 备 数 据 第 6 章 构 造 数 据 第 7 章 声 明 数 据 第 8 章 构 造

More information

入學考試網上報名指南

入學考試網上報名指南 入 學 考 試 網 上 報 名 指 南 On-line Application Guide for Admission Examination 16/01/2015 University of Macau Table of Contents Table of Contents... 1 A. 新 申 請 網 上 登 記 帳 戶 /Register for New Account... 2 B. 填

More information

6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM

6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM CHAPTER 6 SQL SQL SQL 6-1 Table Column Data Type Row Record 1. DBMS 2. DBMS MySQL Microsoft Access SQL Server Oracle 3. ODBC SQL 1. Structured Query Language 2. IBM 3. 1986 10 ANSI SQL ANSI X3. 135-1986

More information

Microsoft Word - 11月電子報1130.doc

Microsoft Word - 11月電子報1130.doc 發 行 人 : 楊 進 成 出 刊 日 期 2008 年 12 月 1 日, 第 38 期 第 1 頁 / 共 16 頁 封 面 圖 話 來 來 來, 來 葳 格 ; 玩 玩 玩, 玩 數 學 在 11 月 17 到 21 日 這 5 天 裡 每 天 一 個 題 目, 孩 子 們 依 據 不 同 年 段, 尋 找 屬 於 自 己 的 解 答, 這 些 數 學 題 目 和 校 園 情 境 緊 緊 結

More information

1 引言

1 引言 中 国 经 济 改 革 研 究 基 金 会 委 托 课 题 能 力 密 集 型 合 作 医 疗 制 度 的 自 动 运 行 机 制 中 国 农 村 基 本 医 疗 保 障 制 度 的 现 状 与 发 展 的 研 究 课 题 主 持 人 程 漱 兰 中 国 人 民 大 学 农 业 与 农 村 发 展 学 院 课 题 组 2004 年 4 月 2005 年 4 月 1 课 题 组 成 员 名 单 主 持

More information

<4D6963726F736F667420506F776572506F696E74202D20C8EDBCFEBCDCB9B9CAA6D1D0D0DEBDB2D7F92E707074>

<4D6963726F736F667420506F776572506F696E74202D20C8EDBCFEBCDCB9B9CAA6D1D0D0DEBDB2D7F92E707074> 软 件 架 构 师 研 修 讲 座 胡 协 刚 软 件 架 构 师 UML/RUP 专 家 szjinco@public.szptt.net.cn 中 国 软 件 架 构 师 网 东 软 培 训 中 心 小 故 事 : 七 人 分 粥 当 前 软 件 团 队 的 开 发 现 状 和 面 临 的 问 题 软 件 项 目 的 特 点 解 决 之 道 : 从 瀑 布 模 型 到 迭 代 模 型 解 决 项

More information

Microsoft Word - 第四組心得.doc

Microsoft Word - 第四組心得.doc 徐 婉 真 這 四 天 的 綠 島 人 權 體 驗 營 令 我 印 象 深 刻, 尤 其 第 三 天 晚 上 吳 豪 人 教 授 的 那 堂 課, 他 讓 我 聽 到 不 同 於 以 往 的 正 義 之 聲 轉 型 正 義, 透 過 他 幽 默 熱 情 的 語 調 激 起 了 我 對 政 治 的 興 趣, 願 意 在 未 來 多 關 心 社 會 多 了 解 政 治 第 一 天 抵 達 綠 島 不 久,

More information

VASP应用运行优化

VASP应用运行优化 1 VASP wszhang@ustc.edu.cn April 8, 2018 Contents 1 2 2 2 3 2 4 2 4.1........................................................ 2 4.2..................................................... 3 5 4 5.1..........................................................

More information

, (), 15,,,,, 2,,,1000 2,,, 5, ;, 5,,3,,,4 2,,, :, , , ,

, (), 15,,,,, 2,,,1000 2,,, 5, ;, 5,,3,,,4 2,,, :, , , , ,,,,,,, 1924 1927,,,,, 1993 5 (1988 ), 1 2002 2 1927, (), 15,,,,, 2,,,1000 2,,, 5, ;, 5,,3,,,4 2,,, :,1927 4 16,1927 4 19,1927 4 18,1927 4 16 2 ,,,,,,,,, 14,, 15,,, 10,, 3, 6,,,,,,,,,,,,, 1,,, 30,,,,2,15

More information

Microsoft Word - template.doc

Microsoft Word - template.doc HGC efax Service User Guide I. Getting Started Page 1 II. Fax Forward Page 2 4 III. Web Viewing Page 5 7 IV. General Management Page 8 12 V. Help Desk Page 13 VI. Logout Page 13 Page 0 I. Getting Started

More information

PowerPoint Presentation

PowerPoint Presentation TOEFL Practice Online User Guide Revised September 2009 In This Guide General Tips for Using TOEFL Practice Online Directions for New Users Directions for Returning Users 2 General Tips To use TOEFL Practice

More information

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7.

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes 包管理理 工具 Helm 蔺礼强 Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes

More information

2005 5,,,,,,,,,,,,,,,,, , , 2174, 7014 %, % 4, 1961, ,30, 30,, 4,1976,627,,,,, 3 (1993,12 ),, 2

2005 5,,,,,,,,,,,,,,,,, , , 2174, 7014 %, % 4, 1961, ,30, 30,, 4,1976,627,,,,, 3 (1993,12 ),, 2 3,,,,,, 1872,,,, 3 2004 ( 04BZS030),, 1 2005 5,,,,,,,,,,,,,,,,, 1928 716,1935 6 2682 1928 2 1935 6 1966, 2174, 7014 %, 94137 % 4, 1961, 59 1929,30, 30,, 4,1976,627,,,,, 3 (1993,12 ),, 2 , :,,,, :,,,,,,

More information

東莞工商總會劉百樂中學

東莞工商總會劉百樂中學 /2015/ 頁 (2015 年 版 ) 目 錄 : 中 文 1 English Language 2-3 數 學 4-5 通 識 教 育 6 物 理 7 化 學 8 生 物 9 組 合 科 學 ( 化 學 ) 10 組 合 科 學 ( 生 物 ) 11 企 業 會 計 及 財 務 概 論 12 中 國 歷 史 13 歷 史 14 地 理 15 經 濟 16 資 訊 及 通 訊 科 技 17 視 覺

More information

ebook140-8

ebook140-8 8 Microsoft VPN Windows NT 4 V P N Windows 98 Client 7 Vintage Air V P N 7 Wi n d o w s NT V P N 7 VPN ( ) 7 Novell NetWare VPN 8.1 PPTP NT4 VPN Q 154091 M i c r o s o f t Windows NT RAS [ ] Windows NT4

More information

Guide to Install SATA Hard Disks

Guide to Install SATA Hard Disks SATA RAID 1. SATA. 2 1.1 SATA. 2 1.2 SATA 2 2. RAID (RAID 0 / RAID 1 / JBOD).. 4 2.1 RAID. 4 2.2 RAID 5 2.3 RAID 0 6 2.4 RAID 1.. 10 2.5 JBOD.. 16 3. Windows 2000 / Windows XP 20 1. SATA 1.1 SATA Serial

More information

Microsoft Word - (web)_F.1_Notes_&_Application_Form(Chi)(non-SPCCPS)_16-17.doc

Microsoft Word - (web)_F.1_Notes_&_Application_Form(Chi)(non-SPCCPS)_16-17.doc 聖 保 羅 男 女 中 學 學 年 中 一 入 學 申 請 申 請 須 知 申 請 程 序 : 請 將 下 列 文 件 交 回 本 校 ( 麥 當 勞 道 33 號 ( 請 以 A4 紙 張 雙 面 影 印, 並 用 魚 尾 夾 夾 起 : 填 妥 申 請 表 並 貼 上 近 照 小 學 五 年 級 上 下 學 期 成 績 表 影 印 本 課 外 活 動 表 現 及 服 務 的 證 明 文 件 及

More information

1.ai

1.ai HDMI camera ARTRAY CO,. LTD Introduction Thank you for purchasing the ARTCAM HDMI camera series. This manual shows the direction how to use the viewer software. Please refer other instructions or contact

More information

1.2 资 金 的 管 理 1.1 权 利 义 务 来 源 MOU 1.3 数 据 的 使 用 和 保 护 2 国 际 空 间 站 资 源 分 配 方 案 54

1.2 资 金 的 管 理 1.1 权 利 义 务 来 源 MOU 1.3 数 据 的 使 用 和 保 护 2 国 际 空 间 站 资 源 分 配 方 案 54 第 29 卷 第 12 期 全 球 科 技 经 济 瞭 望 Vol. 29 No. 12 2014 年 12 月 Global Science, Technology and Economy Outlook Dec. 2014 刘 阳 子 ( 中 国 科 学 技 术 信 息 研 究 所, 北 京 ) 摘 要 : 空 间 探 索 既 复 杂 艰 巨 又 耗 资 甚 大, 因 此, 世 界 各 国 无

More information

摘 要 互 联 网 的 勃 兴 为 草 根 阶 层 书 写 自 我 和 他 人 提 供 了 契 机, 通 过 网 络 自 由 开 放 的 平 台, 网 络 红 人 风 靡 于 虚 拟 世 界 近 年 来, 或 无 心 插 柳, 或 有 意 噱 头, 或 自 我 表 达, 或 幕 后 操 纵, 网 络

摘 要 互 联 网 的 勃 兴 为 草 根 阶 层 书 写 自 我 和 他 人 提 供 了 契 机, 通 过 网 络 自 由 开 放 的 平 台, 网 络 红 人 风 靡 于 虚 拟 世 界 近 年 来, 或 无 心 插 柳, 或 有 意 噱 头, 或 自 我 表 达, 或 幕 后 操 纵, 网 络 上 海 外 国 语 大 学 硕 士 学 位 论 文 论 文 题 目 从 偶 像 符 号 的 消 解 到 消 费 符 号 的 建 构 网 络 红 人 的 形 象 变 迁 研 究 学 科 专 业 传 播 学 届 别 2013 届 姓 名 孙 清 导 师 王 玲 宁 I 摘 要 互 联 网 的 勃 兴 为 草 根 阶 层 书 写 自 我 和 他 人 提 供 了 契 机, 通 过 网 络 自 由 开 放 的

More information

untitled

untitled Ogre Rendering System http://antsam.blogone.net AntsamCGD@hotmail.com geometry systemmaterial systemshader systemrendering system API API DirectX OpenGL API Pipeline Abstraction API Pipeline Pipeline configurationpipeline

More information

本科毕业设计(论文)工作细则&撰写规范

本科毕业设计(论文)工作细则&撰写规范 ...1...1...1...1...2...3...3...3...4...4...5...8...9 1 1. 2. 3. 4. 5. 1. 2. 3. 4. 5. 6. 7. 1. 2. 3. 4. 5. 6. 7. 8. - 1 - 2 1 I. II. A. B. C. 2. I. A. B. C. D. E. F. II. A. B. C. III. A. B. IV. A. B. -

More information

Microsoft Word - SupplyIT manual 3_cn_david.doc

Microsoft Word - SupplyIT manual 3_cn_david.doc MR PRICE Supply IT Lynette Rajiah 1 3 2 4 3 5 4 7 4.1 8 4.2 8 4.3 8 5 9 6 10 6.1 16 6.2 17 6.3 18 7 21 7.1 24 7.2 25 7.3 26 7.4 27 7.5 28 7.6 29 7.7 30 7.8 31 7.9 32 7.10 32 7.11 33 7.12 34 1 7.13 35 7.14

More information

VHDL(Statements) (Sequential Statement) (Concurrent Statement) VHDL (Architecture)VHDL (PROCESS)(Sub-program) 2

VHDL(Statements) (Sequential Statement) (Concurrent Statement) VHDL (Architecture)VHDL (PROCESS)(Sub-program) 2 VHDL (Statements) VHDL(Statements) (Sequential Statement) (Concurrent Statement) VHDL (Architecture)VHDL (PROCESS)(Sub-program) 2 (Assignment Statement) (Signal Assignment Statement) (Variable Assignment

More information

TX-NR3030_BAS_Cs_ indd

TX-NR3030_BAS_Cs_ indd TX-NR3030 http://www.onkyo.com/manual/txnr3030/adv/cs.html Cs 1 2 3 Speaker Cable 2 HDMI OUT HDMI IN HDMI OUT HDMI OUT HDMI OUT HDMI OUT 1 DIGITAL OPTICAL OUT AUDIO OUT TV 3 1 5 4 6 1 2 3 3 2 2 4 3 2 5

More information

1 SIGMA Lab Sydney IntelliGent MultimediA Laboratory Electrical and Information Engineering department EIE Ph.D. M.Phil. 1.QS (Associate Profess

1 SIGMA Lab Sydney IntelliGent MultimediA Laboratory Electrical and Information Engineering department EIE Ph.D. M.Phil. 1.QS (Associate Profess SIGMA Lab October 31, 2018 1 1. 2. 3. 4. Q&A (a) (b) SIGMA Lab (c) (d) 5. (a) (b) i. ii. funding iii. CSC (c) 6. 1 1 1 SIGMA Lab Sydney IntelliGent MultimediA Laboratory Electrical and Information Engineering

More information

M M. 20

M M. 20 37 1 Vol. 37 No.1 2 0 1 6 1 TSINGHUA JOURNAL OF EDUCATION Jan. 2 0 1 6 4. 0 100872 1. 0 2. 0 3. 0 4. 0 4. 0 4. 0 G640 A 1001-4519 2016 01-0006 - 10 DOI 10. 14138 /j. 1001-4519. 2016. 01. 000610 11-12 18

More information

1

1 Activity- based Cost Management: A New Mode of Medical cost Management () 1 Activity - based Cost Management A New Mode of Medical cost Management Abstract With the development of medical market, the defects

More information

提纲 Classical Problems of Synchronization 1 Classical Problems of Synchronization 2 3 4

提纲 Classical Problems of Synchronization 1 Classical Problems of Synchronization 2 3 4 第 6 章 Processe Synchronization2( 进程同步 2) 中国科学技术大学计算机学院 2009 年 10 月 28 日 提纲 Classical Problems of Synchronization 1 Classical Problems of Synchronization 2 3 4 Outline Classical Problems of Synchronization

More information

FY.DOC

FY.DOC 高 职 高 专 21 世 纪 规 划 教 材 C++ 程 序 设 计 邓 振 杰 主 编 贾 振 华 孟 庆 敏 副 主 编 人 民 邮 电 出 版 社 内 容 提 要 本 书 系 统 地 介 绍 C++ 语 言 的 基 本 概 念 基 本 语 法 和 编 程 方 法, 深 入 浅 出 地 讲 述 C++ 语 言 面 向 对 象 的 重 要 特 征 : 类 和 对 象 抽 象 封 装 继 承 等 主

More information

( ,538,000)

( ,538,000) ( 6154256,538,000) 4300 1.1 12 13 548 .2 ()915620 (92142796.8.1796 ()85) () OO 14 15 548 91 687298 190199. () 9110249139055.. 15943. 43 20%.271329 56 () 155 14 92 1690. 16 17 548 15514 () 1. 1988 2 22232

More information

國立中山大學學位論文典藏

國立中山大學學位論文典藏 I II III IV The theories of leadership seldom explain the difference of male leaders and female leaders. Instead of the assumption that the leaders leading traits and leading styles of two sexes are the

More information

CC213

CC213 : (Ken-Yi Lee), E-mail: feis.tw@gmail.com 9 [P.11] : Dev C++ [P.12] : http://c.feis.tw [P.13] [P.14] [P.15] [P.17] [P.23] Dev C++ [P.24] [P.27] [P.34] C / C++ [P.35] 10 C / C++ C C++ C C++ C++ C ( ) C++

More information