===================1. Linux 多线程概述1.1 什么是线程 线程( thread)是包含在进程内部的顺序执行流,是进程中的实际运作单位,也是操作 系统能够进行调度的最小单位。一个进程中可以并发多条线程,每条线程并行执行不同的任 务。1.2 线程与进程的关系 线程与进程的关系可以归结为以下几点: a. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个主线程; b. 资源分配给进程,同一进程的所有线程共享该进程的所有资源; c. 线程作为调度和分配的基本单位,进程作为拥有资源的基本单位; d. 进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源; e. 在创建或撤销进程时,由于系统要为之分配和回收资源,导致系统的开销大于创建或撤销线程时的开销。 1.3 为什么要使用多线程 多进程程序结构和多线程程序结构有很大的不同,多线程程序结构相对于多进程程序结构有以下的优势: ( 1)方便的通信和数据交换 线程间有方便的通信和数据交换机制。对不同进程来说,它们具有独立的数据空间,要 进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然, 由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用, 这不仅快捷,而且方便。 ( 2)更高效的利用 CPU 使用多线程可以加快应用程序的响应。这对图形界面的程序尤其有意义, 假如一个操作 耗时很长, 那么整个系统都会等它操作,此时程序不会响应键盘、鼠标、菜单等操作,而使 用多线程技术,将耗时长的操作置于一个新的线程, 就可以避免这种尴尬情况的发生。 同时多线程使多 CPU 系统更加有效。操作系统会保证当线程数不大于 CPU 数目时,不 同的线程运行于不同的 CPU 上。 =====================2. POSIX Threads 概述 从历史上看, 众多软件供应商都为自己的产品实现了多线程库的专有版本。这些线程库 实现彼此独立并有很大差别,导致程序员难以开发可移植的多线程应用程序,因此必须要确 立一个规范的编程接口标准来充分利用多线程所提供的优势, POSIX Threads 就是这样一个 规范的多线程标准接口。 POSIX Threads(通常简称为 Pthreads)定义了创建和操纵线程的一套 API 接口, 一般 用于 Unix-like POSIX 系统中(如 FreeBSD、 GNU/Linux、 OpenBSD、 Mac OS 等系统)。 Linux 最早的线程库并不是使用 Pthreads。当 Linux 最初开发时,在内核中并不能真正 支持线程。它是通过 clone()系统调用将进程作为可调度的实体。而 LinuxThreads 项目使用 这个调用来完成在用户空间模拟对线程的支持。不幸的是,这种方法有一些缺点,尤其是在 信号处理、调度和进程间同步原语方面都存在问题。另外,这个线程模型也不符合 POSIX 的要求。 Pthreads 接口可以根据功能划分四个组: a. 线程管理 b. 互斥量 c. 条件变量 d. 同步 编写 Pthreads 多线程的程序时, 源码只需要包含 pthread.h 头文件就可以使用 Pthreads 库中的所有类型及函数: #include 在编译 Pthread 程序时在编译和链接过程中需要加上-pthread 参数:LDFLAGS += -pthread ===========3. 线程管理 线程管理包含了线程的创建、终止、等待、分离、设置属性等操作。3.1 线程 ID 线程 ID 可以看作为线程的句柄,用来引用一个线程。Pthreads 线程通过一个 pthread_t 类型的 ID 来引用。 线程可以通过调用 pthread_self()函数来获取自己的 ID。 pthread_self()函数原型如下: pthread_t pthread_self(void); 该函数返回调用线程的线程 ID。 由于 pthread_t 类型可能是一个结构体,可以使用 pthread_equal()来比较两个线程 ID 是否相等。 pthread_equal()函数原型如下: int pthread_equal(pthread_t t1, pthread_t t2); 如果 t1 等于 t2,该函数返回一个非 0 值,否则返回 0。 3.2 创建与终止 每个线程都有从创建到终止的生命周期。 3.2.1 创建线程 在进程中创建一个新线程的函数是 pthread_create(),原型如下: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 说明:线程被创建后将立即运行。 返回值说明: 如果pthread_create()调用成功,函数返回0,否则返回一个非0的错误码,下表列出 pthread_create()函数调用时必须 检查的错误码。 ------------------------------------------------------------------------ 表:pthread_create()错误码表 ------------------------------------- 错误码 出错说明 EAGAIN 系统没有创建线程所需的资源 EINVAL attr 参数无效 EPERM 调用程序没有适当的权限来设定调度策略或 attr 指定的参数 ------------------------------------------------------------------------ 参数说明: a. thread 用来指向新创建线程的 ID; b. attr 用来表示一个封装了线程各种属性的属性对象,如果 attr 为 NULL,新线程就使用默认的属性; c. start_routine 是线程开始执行的时候调用的函数的名字, start_routine 函数有一个指向 void 的指针参数, 并有 pthread_create 的第四个参数 arg 指定值,同时 start_routine函数返回一个指向 void 的指针,这个返回 值被 pthread_join 当做退出状态处理; d. arg 为参数 start_routine 指定函数的参数。 3.2.2 终止线程 进程的终止可以通过直接调用 exit()、执行 main()中的 return、或者通过进程的某个其它线程调用 exit()来实现。 在以上任何一种情况发生时,所有的线程都会被终止。如果主线程在创建了其它线程后没有任务需要处理,那么它应该 阻塞等待直到所有线程都结束为止,或者应该调用 pthread_exit(NULL)。 调用 exit()函数会使整个进程终止,而调用 pthread_exit()只会使得调用线程终止,同时在创建的线程的顶层执行 return 线程会隐式地调用 pthread_exit()。 pthread_exit()函数原型如下: void pthread_exit(void *retval); retval 是一个 void 类型的指针,可以将线程的返回值当作 pthread_exit()的参数传入,这个值同样被 pthread_join() 当作退出状态处理。如果进程的最后一个线程调用了 pthread_exit(),进程会带着状态返回值 0 退出。3.3 连接与分离 线程可以分为分离线程(DETACHED)和非分离线程(JOINABLE)两种: a. 分离线程是退出时会释放它的资源的线程; b. 非分离线程退出后不会立即释放资源,需要另一个线程为它调用 pthread_join 函数或者进程退出时才会释放资源。 只有非分离线程才是可连接的,分离线程退出时不会报告它的退出状态。 3.3.1 线程分离 pthread_detach()函数可以将非分离线程设置为分离线程,函数原型如下: int pthread_detach(pthread_t thread); 参数 thread 是要分离的线程的 ID。 线程可以自己来设置分离,也可以由其它线程来设置分离,以下代码线程可设置自身分离: pthread_detach(pthread_self()); 成功返回 0;失败返回一个非 0 的错误码, 下表列出 pthread_detach 的实现必须检查的错误码。 ------------------------------------------------------- 表:pthread_detach 错误码表 ---------------------------- 错误码 出错描述 EINVAL thread 参数所表示的线程不是可分离的线程 ESRCH 没有找到 ID 为 thread 的线程 ------------------------------------------------------- 3.3.2 线程连接 如果一个线程是非分离线程,那么其它线程可调用 pthread_join()函数对非分离线程进行连接。 pthread_join()函数原型如下: int pthread_join(pthread_t thread, void **retval); pthread_join()函数将调用线程挂起,直到参数 thread 指定的目标线程终止运行为止。 参数 retval 的作用是为指向线程的返回值的指针提供一个位置, 这个返回值是目标线程调用 pthread_exit() 或者 return 后所返回的值。当目标线程无需返回时可使用 NULL 值,调用线程如果不需对目标线程的返回状态 进行检查可直接将 retval 赋值为 NULL。 如果 pthread_join()成功调用,它将返回 0 值,如果不成功, pthread_join()返回一个非 0的错误码, 下表 列出 pthread_join()的实现必须检查的错误码。 --------------------------------------------------------- pthread_join 错误码表 ------------------------------ 错误码 出错描述 EINVAL thread 参数所表示的线程不是可连接的线程 ESRCH 没有找到 ID 为 thread 的线程 -------------------------------------------------------- 举例子。 3.4 线程属性 前面介绍的线程创建 pthread_create()函数,pthread_create()函数的第二个参数为pthread_attr_t 类型, 用于设置线程的属性。 线程基本属性包括:栈大小、调度策略和线程状态。通常先创建一个属性对象,然后在属性对象上设置属性的值, 再将属性对象传给pthread_create 函数的第二个参数用来创建含有该属性的线程。一个属性对象可以多次传给 pthread_create()函数,以创建多个含有相同属性的线程。 3.4.1 属性对象 (1)初始化属性对象 pthread_attr_init()函数用于将属性对象使用默认值进行初始化,函数原型如下: int pthread_attr_init(pthread_attr_t *attr); 函数只有一个参数, 是一个指向 pthread_attr_t 的属性对象的指针。成功返回 0, 否则返回一个非 0 的错误码。 (2) 销毁属性对象 销毁属性对象使用 pthread_attr_destroy()函数, 函数原型如下: int pthread_attr_destroy(pthread_attr_t *attr); 函数只有一个参数, 是一个指向 pthread_attr_t 的属性对象的指针。成功返回 0, 否则返回一个非 0 的错误码。 3.4.2 线程状态 线程可以有两种状态,分别是: a. PTHREAD_CREATE_JOINABLE:非分离线程; b. PTHREAD_CREATE_DETACHED:分离线程。 (1)获取线程状态 获取线程状态的函数是 pthread_attr_getdetachstate(),原型如下: int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); 参数 attr 是一个指向已初始化的属性对象的指针, detachstate 是所获取状态值的指针。 成功返回 0,否则返回一个非 0 的错误码。 (2)设置线程状态 设置线程状态的函数是 pthread_attr_setdetachstate(), 原型如下: int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 参数 attr 是一个指向已初始化的属性对象的指针, detachstate 是要设置的值。 成功返回0,否则返回一个非 0 的错误码。 3.4.3 线程栈 每个线程都有一个独立的调用栈,线程的栈大小在线程创建的时候就已经固定下来, Linux 系统线程的默认栈大小为 8MB, 只有主线程的栈大小会在运行过程中自动增长。用户 可以通过属性对象来设置和获取栈大小。 ( 1)获取线程栈 获取线程栈大小的函数是 pthread_attr_getstacksize(),原型如下: int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize); 参数 attr 是一个指向已初始化的属性对象的指针,stacksize 是保存所获取栈大小的指针。 成功返回 0,否则返回一个非 0 的错误码。 ( 2) 设置线程栈 设置线程栈大小的函数是 pthread_attr_setstacksize(),原型如下: int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); 参数 attr 是一个指向已初始化的属性对象的指针, stacksize 是需要设置的栈大小。 成功返回 0,否则返回一个非 0 的错误码。 线程范例3: 举例说明了线程创建及线程属性的使用方法,主线程根据参数给出的线程栈大小来设置线程属性对象, 然后使用剩余参数分别创建线程来实现小写转大写的功能并打印出栈地址。 =========== 4. 线程安全 多线程编程环境中,多个线程同时调用某些函数可能会产生一些错误结果,这些函数称为非线程安全函数。 如果库函数能够在多个线程中同时执行并且不会互相干扰,那么这个库函数就是线程安全( thread-safe)函数。 POSIX.1-2008 规定, 除了下表列出的特定函数外, 所有标准库的函数都应该是线程安全函数。有些库函数 虽然不是线程安全函数,但系统有后缀为_r 的线程安全版本,如strtok_r。 非线程安全的 POSIX 函数 asctime() ftw() getservbyport() putc_unlocked() basename() getc_unlocked() getservent() putchar_unlocked() catgets() getchar_unlocked() getutxent() putenv() crypt() getdate() getutxid() pututxline() ctime() getenv() getutxline() rand() dbm_clearerr() getgrent() gmtime() readdir() dbm_close() getgrgid() hcreate() setenv() dbm_delete() getgrnam() hdestroy() setgrent() dbm_error() gethostent() hsearch() setkey() dbm_fetch() getlogin() inet_ntoa() setpwent() dbm_firstkey() getnetbyaddr() l64a() setutxent() dbm_nextkey() getnetbyname() lgamma() strerror() dbm_open() getnetent() lgammaf() strsignal() dbm_store() getopt() lgammal() strtok() dirname() getprotobyname() localeconv() system() dlerror() getprotobynumber() localtime() ttyname() drand48() getprotoent() lrand48() unsetenv() encrypt() getpwent() mrand48() wcstombs() endgrent() getpwnam() nftw() wctomb() endpwent() getpwuid() nl_langinfo() endutxent() getservbyname() ptsname()========= 5. 互斥量5.1 临界区 在计算机系统中有许多共享资源不允许用户并行使用。例如打印机设备,如果它同时进行两份文档打印, 它的输出就会产生交错,从而都无法获得正确的文档。像打印机这样的共享设备被称为“ 排它性资源”, 因为它一次只能由一个执行流访问。执行流必须以互斥的方式执行访问排它性资源的代码。 临界区是必须以互斥方式执行的代码段,也就是说在临界区的范围内只能有一个活动的执行线程。5.2 什么是互斥量 互斥量(Mutex),又称为互斥锁,是一种用来保护临界区的特殊变量,它可以处于锁定(locked)状态, 也可以处于解锁(unlocked)状态: a. 如果互斥锁是锁定的,就是某个特定的线程正持有这个互斥锁; b. 如果没有线程持有这个互斥锁,那么这个互斥锁就处于解锁状态。 每个互斥锁内部有一个线程等待队列,用来保存等待该互斥锁的线程。当互斥锁处于解锁状态时, 如果 某个线程试图获取这个互斥锁, 那么这个线程就可以得到这个互斥锁而不会阻塞; 当互斥锁处于锁定状态时, 如果某个线程试图获取这个互斥锁, 那么这个线程将阻塞在互斥锁的等待队列内。 互斥量是最简单也是最有效的线程同步机制。程序可以用它来保护临界区,以获得对排它性资源的访问权。 另外,互斥量只能被短时间地持有,使用完临界资源后应立即释放锁。 5.3 创建与销毁 5.3.1 创建互斥量 pthreads 使用 pthread_mutex_t 类型的变量来表示互斥量,同时在使用互斥量进行同步前需要先对它进行初始化, 可以用静态或动态的方式对互斥量进行初始化。 (1)静态初始化 对于静态分配的pthread_mutex_t变量来说,只要将PTHREAD_MUTEX_INITIALIZER赋给变量就行了。 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; (2)动态初始化 对于动态分配或者不使用默认属性的互斥变量来说,需要调用 pthread_mutex_int()函数来执行初始化工作。 pthread_mutex_int()函数原型如下: int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 参数 mutex 是一个指向要初始化的互斥量的指针; 参数 attr 传递 NULL 来初始化一个带有默认属性的互斥量,否则就要用类似于线程属性对象所使用的方法, 先创建互斥量属性对象,再用该属性对象来创建互斥量。 函数成功返回 0,否则返回一个非 0 的错误码, 表列出 pthread_mutex_init 出错的错误码。 pthread_mutex_ nit 错误码表(0) 错误码 出错描述 EAGAIN 系统缺乏初始化互斥量所需的非内存资源 ENOMEM 系统缺乏初始化互斥量所需的内存资源 EPERM 调用程序没有适当的优先级 注意:静态初始化程序通常比调用 pthread_mutex_init 更有效,而且在任何线程开始执行之前,确保变量被初始化一次。 以下代码用来动态地初始化默认属性的互斥量 mylock: int error; pthread_mutex_t mylock; if (error = pthread_mutex_init(&mylock, NULL)) /* 初始化成功返回0,否则返回一个非0的错误码 */ fprintf(stderr, "Failed to initialize mylock : %s\n", strerror(error)); 5.3.2 销毁互斥量 销毁互斥量使用 pthread_mutex_destroy()函数,原型如下: int pthread_mutex_destroy(pthread_mutex_t *mutex); 参数 mutex 指向要销毁的互斥量。 以下代码销毁了 mylock 互斥量: int error; pthread_mutex_t mylock; if (error = pthread_mutex_destroy(&mylock)) fprintf(stderr, "Failed to destroy mylock : %s\n", strerror(error)); 5.4 加锁与解锁 5.4.1 加锁 线程试图锁定互斥量的过程称之为加锁。 pthreads 中有两个试图锁定互斥量的函数, pthread_mutex_lock()和 pthread_mutex_trylock()。 pthread_mutex_lock()函数会一直阻塞到互斥量可用为止,而 pthread_mutex_trylock()则尝试加锁, 但通常会立即返回。 函数原型如下: int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); 参数 mutex 是需要加锁的互斥量。函数成功返回 0,否则返回一个非 0 的错误码,其中 在另一个线程已持有锁的情况下,调用 pthread_mutex_trylock()函数时错误码为 EBUSY。 5.4.2 解锁 解锁是线程将互斥量由锁定状态变为解锁状态。 pthread_mutex_unlock()函数用来释放指定的互斥量。函数原型如下: int pthread_mutex_unlock(pthread_mutex_t *mutex); 参数 mutex 是需要解锁的互斥量。函数成功返回 0,否则返回一个非 0 的错误码。 只有在线程进入临界区之前正确地获取了适当的互斥量, 才能在离开临界区时释放互斥量。 以下伪代码展示了互斥量保护临界区的基本用法: pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mylock); 临界区代码..... pthread_mutex_unlock(&mylock); 线程范例 4 是使用互斥量来保证多线程同时输出顺序的例子,互斥量能保证只有获取资源的线程打印完,别的线程才能打印, 从而避免了打印乱序的问题。 5.5 死锁和避免 5.5.1 什么是死锁 死锁是指两个或两个以上的执行序列在执行过程中, 因争夺资源而造成的一种互相等待的现象。 例如: 一个线程 T1 已锁定了一个资源 R1, 又想去锁定资源 R2,而此时另一个线程T2已锁定了资源 R2, 却想去锁定资源 R1。这两个线程都想得到对方的资源,而又不愿释放自己的资源, 结果就是两个线程都在等待而无法执行。 程序实例: 程序示例了死锁发生的情况,程序创建了两个线程,第一个线程先获取 mutexA 锁,再获取 mutexB 锁;第二个线程先获取 mutexB 后获取 mutexA,这时死锁就可能发生。 5.5.2 死锁的避免 当多个线程需要相同的一些锁, 但是按照不同的顺序加锁, 死锁就很容易发生, 如果能确保所有的线程都是 按照相同的顺序获得锁,那么死锁就不会发生。 例如,规定程序内有三个互斥锁的加锁顺序为 mutexA->mutexB->mutexC, 则线程 t1、 t2、 t3 线程操作伪代码如下所示: t1 t2 t3 lock(mutexA) lock(mutexA) lock(mutexB) lock(mutexB) lock(mutexC) lock(mutexC) lock(mutexC) 5.6 条件变量 5.6.1 为什么需要条件变量 在多线程编程中仅使用互斥锁来完成互斥是不够用的, 如以下情形: 假设有两个线程 t1 和 t2, 需要这个两个线程循环对一个共享变量 sum 进行自增操作, 那么 t1 和 t2 只需要使用互斥量即可保证操作正确完成,线程执行代码如所示: pthread_mutex_t sumlock= PTHREAD_MUTEX_INITIALIZER; void * t1t2(void) { pthread_mutex_lock(&sumlock); sum++; pthread_mutex_unlock(&sumlock); } 如果这时需要增加另一个线程 t3,需要 t3 在 count 大于 100 时将 count 值重新置 0 值, 那么可以 t3 可以实现如下: void * t3 (void) { pthread_mutex_lock(&sumlock); if (sum >= 100) { sum = 0; pthread_mutex_unlock(&sumlock); } else { pthread_mutex_unlock(&sumlock); usleep(100); } } 以上代码存在以下问题: 1) sum 在大多数情况下不会到达 100, 那么对 t3 的代码来说,大多数情况下, 走的是 else 分支, 只是 lock 和 unlock,然后 sleep()。 这浪费了 CPU 处理时间。 2) 为了节省 CPU 处理时间, t3 会在探测到 sum 没到达 100 的时候 usleep()一段时间。 这样却又带来另外一个问题, 亦即 t3 响应速度下降。 可能在 sum 到达 200 的时候, t3 才会 醒过来。 这样时间与效率出现了矛盾,而条件变量就是解决这个问题的好方法。 5.6.2 创建与销毁 1. 创建条件变量 Pthreads 用 pthread_cond_t 类型的变量来表示条件变量。程序必须在使用 pthread_cond_t变量之前对其进行初始化。 (1) 静态初始化 对于静态分配的变量可以简单地将 PTHREAD_COND_INITIALIZER 赋值给变量来初始化默认行为的条件变量。 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; (2)动态初始化 对动态分配或者不使用默认属性的条件变量来说可以使用 pthread_cond_init()来初始化。 函数原型如下: int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 参数 cond 是一个指向需要初始化 pthread_cond_t 变量的指针,参数 attr 传递 NULL 值时, pthread_cond_init()将 cond 初始化为默认属性的条件变量。 函数成功将返回 0;否则返回一个非 0 的错误码。 静态初始化程序通常比调用 pthread_cond_init()更有效,而且在任何线程开始执行之前,确保变量被执行一次。 以下代码示例了条件变量的初始化。 pthread_cond_t cond; int error; if (error = pthread_cond_init(&cond, NULL)); fprintf(stderr, "Failed to initialize cond : %s\n", strerror(error)); 2. 销毁条件变量 函数 pthread_cond_destroy()用来销毁它参数所指出的条件变量,函数原型如下: int pthread_cond_destroy(pthread_cond_t *cond); 函数成功调用返回 0,否则返回一个非 0 的错误码。以下代码演示了如何销毁一个条件变量。 pthread_cond_t cond; int error; if (error = pthread_cond_destroy(&cond)) fprintf(stderr, "Failed to destroy cond : %s\n", strerror(error)); 5.6.3 等待与通知 1. 等待 条件变量是与条件测试一起使用的,通常线程会对一个条件进行测试,如果条件不满足就会调用条件等待函数来 等待条件满足。 条件等待函数有 pthread_cond_wait() 和 pthread_cond_timedwait()两个,函数原型如下: int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); pthread_cond_wait()函数在条件不满足时将一直等待, 而 pthread_cond_timedwait()将只等待一段时间。 参数 cond 是一个指向条件变量的指针,参数 mutex 是一个指向互斥量的指针,线程在调用前应该拥有这个互斥量, 当线程要加入条件变量的等待队列时,等待操作会使线程释放这个互斥量。 pthread_timedwait()的第三个参数 abstime 是一个指向返回时间的指针,如果条件变量通知信号没有在此等待时间 之前出现,等待将超时退出, abstime 是个绝对时间,而不是时间间隔。 以上函数成功调用返回 0,否则返回非 0 的错误码,其中 pthread_cond_timedwait()函数如果 abstime 指定的时间 到期,错误码为 ETIMEOUT。 以下代码使得线程进入等待,直到收到通知并且满足 a 大于等于 b 的条件。 pthread_mutex_lock(&mutex) while(a pthread_cond_wait(&cond, &mutex) pthread_mutex_unlock(&mutex) 2. 通知 当另一个线程修改了某参数可能使得条件变量所关联的条件变成真时,它应该通知一个或者多个等待在条件变量等待 队列中的线程。 条件通知函数有 pthread_cond_signal()和 pthread_cond_broadcast()函数,其中 pthread_cond_signal 函数可以 唤醒一个在条件变量等待队列等待的线程,而 pthread_cond_broadcast函数可以所有在条件变量等待队列等待的线程。 函数原型如下: int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); 参数 cond 是一个指向条件变量的指针。函数成功返回 0,否则返回一个非 0 的错误码。 线程范例: 解决5.6.1的问题。 相关源代码【linuxPthread.rar】 12-11 13:22