pthread(POSIX thread)库是个标准C/C++多线程API库,线程没有父子之分。与标准 fork() 相比,线程带来的开销很小。内核无需单独复制进程的内存空间或文件描述符等等。这就节省了大量的 CPU 时间,使得线程创建比新进程创建快上十到一百倍。因为这一点,可以大量使用线程而无需太过于担心带来的 CPU 或内存不足。
1.1 线程的创建和销毁
int pthread_create(
pthread_t* thread, //threadId
const pthread_attr_t * attr,
void* (*start_routine)(void *), //thread_function
void* arg //thread_function's parameter
);
参数:
一个 POSIX 线程的简单示例程序:
#include
#include
#include
void* run(void* msg)
{
sleep(20);
printf("%s\n", msg);
}
int main()
{
pthread_t pid1, pid2; //unsigned long int
int iret1 = 0, iret2 = 0;
pthread_create(&pid1, NULL, run, "Hello, pthread! Good try 1");
if(iret1)
{
fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);
exit(EXIT_FAILURE);
}
pthread_create(&pid2, NULL, run, "Hello, pthread! Good try 2");
if(iret2)
{
fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);
exit(EXIT_FAILURE);
}
printf("pthread_create() for thread 1 returns: %d\n",iret1);
printf("pthread_create() for thread 2 returns: %d\n",iret2);
if ( pthread_join(pid1, NULL) || pthread_join(pid2, NULL) ) {
printf("error joining thread.");
abort();
}
return 0;
}
问题:
上例thread_function() 花了 20 秒才完成。在 thread_function() 结束很久之前,主线程就已经调用了 pthread_join()。如果发生这种情况,主线程将中断(转向睡眠)然后等待 thread_function() 完成。当 thread_function() 完成后, pthread_join() 将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使用 pthread_join() 合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清理,最终会导致 pthread_create() 调用失败。
#include
#include
#include
enum {MAX_THREAD_NUMBER = 3}; //C语言const不是常量的意思,不能用const定义
int number = 0;
pthread_t pid[MAX_THREAD_NUMBER] = {0};
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
void* Counter(void* nth)
{
int count = 10;
//pthread_mutex_lock(&mymutex);
int local = number;
for (count = 0; count < 10; count++)
{
local++;
printf("Counter: %d, thread id : %d, thread number :%ld\n", local, *(int*)nth, pthread_self());
}
number = local;
//pthread_mutex_unlock(&mymutex);
}
int main()
{
int i = 0;
for(i = 0; i < MAX_THREAD_NUMBER; i++)
{
pthread_create(&pid[i], NULL, &Counter, (void*)&i);
}
for(i = 0; i < MAX_THREAD_NUMBER; i++)
{
pthread_join(pid[i], NULL);
}
return 0;
}
编译上面的代码,我们发现多线程并没有累加全局number,是因为这个变量被各自线程局部化了,如何共享全局变量呢?使用互斥对象
两个线程不能同时对同一个互斥对象(Mutex)加锁。
锁定了互斥对象的线程能够存取复杂的数据结构,而不必担心同时会有其它线程干扰直到互斥对象被解锁为止。
互斥对象是这样工作的:如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 的所有线程都将进入睡眠状态,这些睡眠的线程将“排队”访问这个互斥对象。
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init( pthread_mutex_t *mymutex, const pthread_mutexattr_t *attr)
以上2中方式,一种是静态初始化,另外一种是动态初始化。
一旦使用 pthread_mutex_init() 初始化了互斥对象,就应使用 pthread_mutex_destroy() 消除它。pthread_mutex_destroy() 接受一个指向 pthread_mutext_t 的指针作为参数,并释放创建互斥对象时分配给它的任何资源。请注意, pthread_mutex_destroy() 不会 释放用来存储 pthread_mutex_t 的内存。释放自己的内存完全取决于您。还必须注意一点,pthread_mutex_init() 和 pthread_mutex_destroy() 成功时都返回零。
pthread_mutex_lock(pthread_mutex_t *mutex)
pthread_mutex_lock() 接受一个指向互斥对象的指针作为参数以将其锁定。如果碰巧已经锁定了互斥对象,调用者将进入睡眠状态。函数返回时,将唤醒调用者(显然)并且调用者还将保留该锁。函数调用成功时返回零,失败时返回非零的错误代码。
pthread_mutex_unlock(pthread_mutex_t *mutex)
pthread_mutex_unlock() 与 pthread_mutex_lock() 相配合,它把线程已经加锁的互斥对象解锁。始终应该尽快对已加锁的互斥对象进行解锁(以提高性能)。并且绝对不要对您未保持锁的互斥对象进行解锁操作(否则,pthread_mutex_unlock() 调用将失败并带一个非零的 EPERM 返回值)。
pthread_mutex_trylock(pthread_mutex_t *mutex)
当线程正在做其它事情的时候(由于互斥对象当前是锁定的),如果希望锁定互斥对象,这个调用就相当方便。调用 pthread_mutex_trylock() 时将尝试锁定互斥对象。如果互斥对象当前处于解锁状态,那么您将获得该锁并且函数将返回零。然而,如果互斥对象已锁定,这个调用也不会阻塞。当然,它会返回非零的 EBUSY 错误值。然后可以继续做其它事情,稍后再尝试锁定。
2.3.1 执行顺序
假设主线程将创建三个新线程:线程 a、线程 b 和线程 c。假定首先创建线程 a,然后是线程 b,最后创建线程 c。
pthread_create( &thread_a, NULL, thread_function, NULL);
pthread_create( &thread_b, NULL, thread_function, NULL);
pthread_create( &thread_c, NULL, thread_function, NULL);
在第一个 pthread_create() 调用完成后,可以假定线程 a 不是已存在就是已结束并停止。第二个 pthread_create() 调用后,主线程和线程 b 都可以假定线程 a 存在(或已停止)。
然而,就在第二个 create() 调用返回后,主线程无法假定是哪一个线程(a 或 b)会首先开始运行。虽然两个线程都已存在,线程 CPU 时间片的分配取决于内核和线程库。至于谁将首先运行,并没有严格的规则。尽管线程 a 更有可能在线程 b 之前开始执行,但这并无保证。对于多处理器系统,情况更是如此。如果编写的代码假定在线程 b 开始执行之前实际上执行线程 a 的代码,那么,程序最终正确运行的概率是 99%。或者更糟糕,程序在您的机器上 100% 地正确运行,而在您客户的四处理器服务器上正确运行的概率却是零。
2.3.2 全局变量引用
现在来看另一个假想的例子。假设有许多线程,他们都正在执行下列代码:
number = number + 1;
如果示例2没有采用线程的局部变量,而是直接用全局的nubmer进行自增,那么需要考虑用pthread_mutex_lock() 和 pthread_mutex_unlock() 调用吗?答案是:最好要!也许有人会说“不”。编译器极有可能把上述赋值语句编译成一条机器指令。大家都知道,不可能"半途"中断一条机器指令。即使是硬件中断也不会破坏机器指令的完整性。基于以上考虑,很可能倾向于完全省略 pthread_mutex_lock() 和 pthread_mutex_unlock() 调用。不要这样做,原因:使用单条内嵌汇编操作码在单处理器系统上可能不会有什么问题。每个加一操作都将完整地进行,并且多半会得到期望的结果。但是多处理器系统则截然不同。在多 CPU 机器上,两个单独的处理器可能会在几乎同一时刻(或者,就在同一时刻)执行上述赋值语句。不要忘了,这时对内存的修改需要先从 L1 写入 L2 高速缓存、然后才写入主存。(SMP 机器并不只是增加了处理器而已;它还有用来仲裁对 RAM 存取的特殊硬件。)最终,根本无法搞清在写入主存的竞争中,哪个 CPU 将会"胜出"。要产生可预测的代码,应使用互斥对象。互斥对象将插入一道"内存关卡",由它来确保对主存的写入按照线程锁定互斥对象的顺序进行。
场景:如果互斥对象锁住的数据结构(链表)为空,那么获得锁的线程将什么操作都不做,然后释放锁,其他线程重复这个操作将会浪费CPU资源?那有什么方法能提高效率呢,此事,我们引入条件变量。
pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心。
锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。
第一个线程首先调用:pthread_mutex_lock(&mymutex);
然后,它检查了列表。没有找到感兴趣的东西,于是它调用:pthread_cond_wait(&mycond, &mymutex);
然后,pthread_cond_wait() 调用在返回前执行许多操作:pthread_mutex_unlock(&mymutex);
它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程“信号”。一旦接收到“信号”(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:pthread_mutex_lock(&mymutex)
pthread_cond_wait() 知道我们在查找 mymutex “背后”的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。
定义:pthread_cond_t mycond;
初始化:pthread_cond_init(&mycond,NULL);
销毁:pthread_cond_destroy(&mycond);
pthread_cond_wait(&mycond, &mymutex);
请注意,代码在逻辑上应该包含 mycond 和 mymutex。一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据“内部”的一种特殊的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。
pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改共享资源),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改共享资源。此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。
现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并修改了共享资源。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。
现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。
简而言之:如果pthread_cond_wait() 函数不传递这个互斥量参数,那么解锁互斥量只能等到线程阻塞被唤醒从pthread_cond_wait() 函数返回,而唤醒又需要条件改变调用pthread_cond_wait() 函数,条件改变的前提是获得那个保护条件的互斥量的锁,而互斥量解锁又得等到阻塞的线程被唤醒。。死锁了。所以需要传递它使得互斥量的解锁在函数内进行。
对于发送信号和广播,需要注意一点。如果线程更改某些共享数据,而且它想要唤醒所有正在等待的线程,则应使用 pthread_cond_broadcast 调用,如下所示:pthread_cond_broadcast(&mycond);
在某些情况下,活动线程只需要唤醒第一个正在睡眠的线程。假设您只对队列添加了一个工作作业。那么只需要唤醒一个工作程序线程:pthread_cond_wait() ;
#include
#include
#include
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *fun(void *arg)
{
int *var_p = arg;
pthread_mutex_lock(&mutex);
while(*var_p == 0) {
pthread_cond_wait(&cond, &mutex); // wait
}
pthread_mutex_unlock(&mutex);
puts("ok");
}
int main()
{
int var = 0;
pthread_t pid;
pthread_create(&pid, NULL, fun, &var);
getchar();
pthread_mutex_lock(&mutex);
var = 1;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond); // signal
pthread_join(pid, NULL);
return 0;
}
在一个进程中定义的全局或静态变量都是所有线程可见的,即每个线程共同操作一块存储区域。而有时我们可能有这样的需求:对于一个全局变量,每个线程对其的修改只在本线程内有效,各线程之间互不干扰。即每个线程虽然共享这个全局变量的名字,但这个变量的值就像只有在本线程内才会被修改和读取一样。
线程局部存储和线程特有数据都可以实现上述需求。
线程局部存储提供了持久的每线程存储,每个线程都拥有一份对变量的拷贝。线程局部存储中的变量将一直存在,直到线程终止,届时会自动释放这一存储。一个典型的例子就是errno的定义(uClibc-0.9.32),每个线程都有自己的一份errno的拷贝,防止了一个线程获取errno时被其他线程干扰。
要定义一个线程局部变量很简单,只需简单的在全局或静态变量的声明中包含__thread说明符即可。例如:
static __thread int buf[MAX_ERROR_LEN];
这样定义的变量,在一个线程中只能看到本线程对其的修改。
关于线程局部变量的声明和使用,需要注意以下几点:
- 如果变量声明中使用了关键字static或extern,那么关键字__thread必须紧随其后。
- 与一般的全局或静态变量声明一样,线程局部变量在声明时可以设置一个初始值。
- 可以使用C语言取址操作符(&)来获取线程局部变量的地址。
在一个线程中修改另一个线程的局部变量:
__thread变量并不是在线程之间完全隐藏的,每个线程保存自己的一份拷贝,因此每个线程的这个变量的地址不同。但这个地址是整个进程可见的,因此一个线程获得另外一个线程的局部变量的地址,就可以修改另一个线程的这个局部变量。
C++中对__thread变量的使用有额外的限制:
- 在C++中,如果要在定义一个thread-local变量的时候做初始化,初始化的值必须是一个常量表达式。
- __thread只能修饰POD类型,即不带自定义的构造、拷贝、赋值、析构的类型,不能有non-static的protected或private成员,没有基类和虚函数,因此对定义class做了很多限制。但可以改为修饰class指针类型便无需考虑此限制。
上面是C/C++语言实现每线程变量的方式,而POSIX thread使用getthreadspecific和setthreadspecific 组件来实现这一特性,因此编译要加-pthread,但是使用这种方式使用起来很繁琐,并且效率很低。不过我也简单讲一下用法。
使用线程特有数据需要下面几步:
Thread Local变量的具体用法。
函数的原型:
int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
Gcc 4.1.2版本之后,对X86或X86_64支持内置原子操作。就是说,不需要引入第三方库(如pthread)的锁保护,即可对1、2、4、8字节的数值或指针类型,进行原子加/减/与/或/异或等操作。有了这套内置原子操作函数,写程序方便很多。老宋根据Gcc手册中《Using the GNU Compiler Collection (GCC)》章节内容,将__sync_系列17个函数声明整理简化如下:
type __sync_fetch_and_add (type *ptr, type value, ...) // 将value加到*ptr上,结果更新到*ptr,并返回操作之前*ptr的值
type __sync_fetch_and_sub (type *ptr, type value, ...) // 从*ptr减去value,结果更新到*ptr,并返回操作之前*ptr的值
type __sync_fetch_and_or (type *ptr, type value, ...) // 将*ptr与value相或,结果更新到*ptr, 并返回操作之前*ptr的值
type __sync_fetch_and_and (type *ptr, type value, ...) // 将*ptr与value相与,结果更新到*ptr,并返回操作之前*ptr的值
type __sync_fetch_and_xor (type *ptr, type value, ...) // 将*ptr与value异或,结果更新到*ptr,并返回操作之前*ptr的值
type __sync_fetch_and_nand (type *ptr, type value, ...) // 将*ptr取反后,与value相与,结果更新到*ptr,并返回操作之前*ptr的值
type __sync_add_and_fetch (type *ptr, type value, ...) // 将value加到*ptr上,结果更新到*ptr,并返回操作之后新*ptr的值
type __sync_sub_and_fetch (type *ptr, type value, ...) // 从*ptr减去value,结果更新到*ptr,并返回操作之后新*ptr的值
type __sync_or_and_fetch (type *ptr, type value, ...) // 将*ptr与value相或, 结果更新到*ptr,并返回操作之后新*ptr的值
type __sync_and_and_fetch (type *ptr, type value, ...) // 将*ptr与value相与,结果更新到*ptr,并返回操作之后新*ptr的值
type __sync_xor_and_fetch (type *ptr, type value, ...) // 将*ptr与value异或,结果更新到*ptr,并返回操作之后新*ptr的值
type __sync_nand_and_fetch (type *ptr, type value, ...) // 将*ptr取反后,与value相与,结果更新到*ptr,并返回操作之后新*ptr的值
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...) // 比较*ptr与oldval的值,如果两者相等,则将newval更新到*ptr并返回true
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...) // 比较*ptr与oldval的值,如果两者相等,则将newval更新到*ptr并返回操作之前*ptr的值
__sync_synchronize (...) // 发出完整内存栅栏
type __sync_lock_test_and_set (type *ptr, type value, ...) // 将value写入*ptr,对*ptr加锁,并返回操作之前*ptr的值。即,try spinlock语义
void __sync_lock_release (type *ptr, ...) // 将0写入到*ptr,并对*ptr解锁。即,unlock spinlock语义
生产者生产同一类型的商品放到仓库,多个消费者随机的不停消费
#include
#include
#include
#include
#include
#include
#define BUFF_SIZE 16 // 缓存区大小
#define CONSUMER_NUM 3 // 消费者人数
int isStop = 0; // 判断停止线程
struct prodcons
{
int buffer[BUFF_SIZE];
pthread_mutex_t lock;
int readpos,writepos;
pthread_cond_t notEmpty;
pthread_cond_t notFull;
}m_prodconsStruct;
typedef struct prodcons prodcons_t;
void initial(struct prodcons* shareStruct)
{
pthread_mutex_init(&shareStruct->lock,NULL);
pthread_cond_init(&shareStruct->notEmpty,NULL);
pthread_cond_init(&shareStruct->notFull,NULL);
shareStruct->readpos=0;
shareStruct->writepos=0;
memset(shareStruct->buffer, -1, sizeof(int)*BUFF_SIZE); // -1 表示未写入数据
}
void writeIn(prodcons_t *shareStruct,int data)
{
pthread_mutex_lock(&shareStruct->lock);
/*
线程挂起条件:
对写指针而言,缓存是环形。
1.写指针追上读指针表示缓存被写满,此时不可再写,否则会覆盖未读数据;
2.收到线程停止指令;
此处是对已经读过的缓存清零以便下次写时判断是否是写满状态,另一方案读写指针用一直累加的方法判断谁前谁后.
*/
while((shareStruct->writepos == shareStruct->readpos)
&& (shareStruct->buffer[(shareStruct->writepos+1)%BUFF_SIZE] != -1)
&& !isStop)
{
printf("wait for empty space\n");
pthread_cond_wait(&shareStruct->notFull,&shareStruct->lock);
}
if(isStop)
{
pthread_mutex_unlock(&shareStruct->lock);
pthread_exit((void *)2);
}
shareStruct->buffer[shareStruct->writepos % BUFF_SIZE] = data;
printf("writeIn----->%d\n",data);
shareStruct->writepos++;
if(shareStruct->writepos >= BUFF_SIZE)
shareStruct->writepos = 0; // 缓存存满后,从头开始再存
pthread_cond_signal(&shareStruct->notEmpty);
pthread_mutex_unlock(&shareStruct->lock);
}
int readOut(prodcons_t *shareStruct, int threadNum)
{
pthread_mutex_lock(&shareStruct->lock);
while((shareStruct->readpos == shareStruct->writepos)
&& (shareStruct->buffer[(shareStruct->readpos+1)%BUFF_SIZE] == -1)
&& !isStop)
{
printf("wait for data, Consumer Number %d\n", threadNum);
pthread_cond_wait(&shareStruct->notEmpty, &shareStruct->lock);
}
if(isStop)
{
pthread_mutex_unlock(&shareStruct->lock);
exit(EXIT_FAILURE);
//pthread_exit((void *)2);
}
int data = shareStruct->buffer[shareStruct->readpos % BUFF_SIZE];
printf("Consumer Number %d thread : 0x%0x and data = %d\n",threadNum,pthread_self(), data);
shareStruct->buffer[shareStruct->readpos % BUFF_SIZE] = -1; // 读之后清数据
shareStruct->readpos++;
if(shareStruct->readpos >= BUFF_SIZE)
shareStruct->readpos=0; // 缓存读完后,从头再读
pthread_cond_signal(&shareStruct->notFull);
pthread_mutex_unlock(&shareStruct->lock);
return data;
}
void* producer(void* arg)
{
int data = 0;
while(1)
{
writeIn(&m_prodconsStruct, data);
data++;
}
return NULL;
}
void* consumer(void* threadNum)
{
while(1) // C语言无bool类型,也就无true和false
{
readOut(&m_prodconsStruct,*(int*)threadNum);
}
return NULL;
}
int main()
{
int err = -1, i=0;
void* ret;
pthread_t pid_pro;
pthread_t pid_con[CONSUMER_NUM];
// 初始化共享结构体
initial(&m_prodconsStruct);
// 创建生产者和消费者
pthread_create(&pid_pro,NULL,producer, NULL);
for(i=0; i < CONSUMER_NUM; i++)
{
pthread_create(&pid_con[i],NULL,consumer,(void*)&i);
}
// 延迟-----觉得线程能跑多久
struct timeval timeout;
timeout.tv_sec = 0; // 1s
timeout.tv_usec = 1000;
select(0, NULL, NULL, NULL, &timeout);
// 销毁生产者消费者线程
isStop = 1;
pthread_cond_broadcast(&m_prodconsStruct.notEmpty);
pthread_cond_broadcast(&m_prodconsStruct.notFull);
err = pthread_join(pid_pro,&ret);
if(err != 0)
printf("can not join producter thread :0x%0x\n", pid_pro);
else
printf("exit producter thread :0x%0x, exit code: %d\n", pid_pro, *(int*)ret);
for(i = 0; i < CONSUMER_NUM; i++)
{
err = pthread_join(pid_con[i],&ret);
if(err != 0)
printf("can not join consumer thread :0x%0x\n", pid_con[i]);
else
printf("exit consumer thread :0x%0x, exit code: %d\n", pid_con[i], *(int*)ret);
}
printf("end...\n");
exit(0);
}
int pthread_attr_destroy(pthread_attr_t *);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
int pthread_attr_getguardsize(const pthread_attr_t *, size_t *);
int pthread_attr_getinheritsched(const pthread_attr_t *, int *);
int pthread_attr_getschedparam(const pthread_attr_t *,
struct sched_param *);
int pthread_attr_getschedpolicy(const pthread_attr_t *, int *);
int pthread_attr_getscope(const pthread_attr_t *, int *);
int pthread_attr_getstackaddr(const pthread_attr_t *, void **);
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *);
int pthread_attr_init(pthread_attr_t *);
int pthread_attr_setdetachstate(pthread_attr_t *, int);
int pthread_attr_setguardsize(pthread_attr_t *, size_t);
int pthread_attr_setinheritsched(pthread_attr_t *, int);
int pthread_attr_setschedparam(pthread_attr_t *,
const struct sched_param *);
int pthread_attr_setschedpolicy(pthread_attr_t *, int);
int pthread_attr_setscope(pthread_attr_t *, int);
int pthread_attr_setstackaddr(pthread_attr_t *, void *);
int pthread_attr_setstacksize(pthread_attr_t *, size_t);
int pthread_cancel(pthread_t);
void pthread_cleanup_push(void*), void *);
void pthread_cleanup_pop(int);
int pthread_cond_broadcast(pthread_cond_t *);
int pthread_cond_destroy(pthread_cond_t *);
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
int pthread_cond_signal(pthread_cond_t *);
int pthread_cond_timedwait(pthread_cond_t *,
pthread_mutex_t *, const struct timespec *);
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
int pthread_condattr_destroy(pthread_condattr_t *);
int pthread_condattr_getpshared(const pthread_condattr_t *, int *);
int pthread_condattr_init(pthread_condattr_t *);
int pthread_condattr_setpshared(pthread_condattr_t *, int);
int pthread_create(pthread_t *, const pthread_attr_t *,
void *(*)(void *), void *);
int pthread_detach(pthread_t);
int pthread_equal(pthread_t, pthread_t);
void pthread_exit(void *);
int pthread_getconcurrency(void);
int pthread_getschedparam(pthread_t, int *, struct sched_param *);
void* pthread_getspecific(pthread_key_t);
int pthread_join(pthread_t, void **);
int pthread_key_create(pthread_key_t *, void (*)(void *));
int pthread_key_delete(pthread_key_t);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_getprioceiling(const pthread_mutex_t *, int *);
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_setprioceiling(pthread_mutex_t *, int, int *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutexattr_destroy(pthread_mutexattr_t *);
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *,
int *);
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_init(pthread_mutexattr_t *);
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int);
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
int pthread_once(pthread_once_t *, void (*)(void));
int pthread_rwlock_destroy(pthread_rwlock_t *);
int pthread_rwlock_init(pthread_rwlock_t *,
const pthread_rwlockattr_t *);
int pthread_rwlock_rdlock(pthread_rwlock_t *);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
int pthread_rwlock_unlock(pthread_rwlock_t *);
int pthread_rwlock_wrlock(pthread_rwlock_t *);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *,
int *);
int pthread_rwlockattr_init(pthread_rwlockattr_t *);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);
pthread_t
pthread_self(void);
int pthread_setcancelstate(int, int *);
int pthread_setcanceltype(int, int *);
int pthread_setconcurrency(int);
int pthread_setschedparam(pthread_t, int ,
const struct sched_param *);
int pthread_setspecific(pthread_key_t, const void *);
void pthread_testcancel(void);