一、私有数据
1.特殊的变量
多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程共有。在程序设计中有时需要保存线程自己的全局变量,这种特殊的变量仅在某个线程内部有效。举例来说,errno它返回标准的出错代码,errno不应该为局部变量,几乎每个函数都应该可以访问它,但它又不能为全局变量,否则在一个线程里输出的很可能是另一个线程的出错信息。这个问题可以通过创建线程的私有数据(Thread-specific Data ,或TSD)来解决。
2.TSD池(关键数据结构数组)
创建一个TSD就相当于将结构体数组中的某一项设置为”in_use”,并将其索引返回给*key,然后设置destructor函数为destr_function。
3.一键多值
线程私有数据采用了一键多值的技术,。访问数据时都是通过键值来访问。操作线程私有函数的主要函数有4个
#include <pthread.h>
int pthread_key_creat(pthread_key_t *key, void (*destr function) (void*));
int pthread_setspecific(pthread_key_t key, const void * pointer);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete (pthread_key_t key);
pthread_key_creat:从YSD池中分配一项,将其值赋给key供以后访问使用。
pthread_setspecific:将pointer的值(不是内容)与key相关联。
pthread_getspecific:得到与key相关联的数据。
pthread_key_delete:删除一个键。
实现创建和使用线程的私有数据
运行结果
注:两个线程分别将tsd作为线程私有数据,从结果可以看出,两个线程tsd的修改互不干扰。线程终止时使用了pthread_exit(),即使主线程退出了,其他线程也会继续运行。
二、线程同步
1.互斥锁
互斥锁通过锁机制来实现线程间的同步问题。在同一时刻它通常只允许一个线程执行一个关键部分的代码
#include <pthread.h>
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
//初始化一个互斥锁
pthread_mutex_destroy(pthread_mutex_t *mutex);
//注销一个互斥锁
pthread_mutex_lock(pthread_mutex_t *mutex);
//加锁,如果不成功,阻塞等待
pthread_mutex_unlock(pthread_mutex_t *mutex);
//解锁
pthread_mutex_trylock(pthread_mutex_t *mutex);
//测试加锁,如果不成功则立即返回,错误码为EBUSY
[1].初始化互斥锁
除了使用pthread_mutex_init()函数初始化,还可用静态赋值法
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
[2].加锁
用pthread_mutex_lock()加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。
[3].解锁
pthread_mutex_unlock()解锁时,要满足两个条件:a.互斥锁必须处于加锁状态,b.调用本函数的线程必须是给互斥锁加锁的线程。
当一个互斥锁用完后,必须进行清除。
[4]. 注销
清除一个互斥锁意味着释放它所占用的资源。清除锁时要求当前处于开放状态,若锁处于锁定状态,函数返回EBUSY,该函数执行成功时返回0。
2.条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制。使用条件变量主要包括两个动作:一个等待使用资源的线程等待“条件变量被设置为真”;另一个线程在使用完资源后“设置条件为真”,这样就可以保证线程间的同步。
pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
//初始化条件变量
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//基于条件变量阻塞,无条件等待
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespc *abstime);
//阻塞直到指定事件发生,计时等待
pthread_cond_signal(pthread_cond_t *cond);
//解除特定线程的阻塞,存在多个等待线程时,按入队顺序激活其中一个
pthread_cond_broadcast(pthread_cond_t *cond);
//解除所有线程的阻塞
pthread_cond_destroy(pthread_cond_t *cond);
//清除条件变量
[1].初始化
除了使用函数pthread_cond_init()之外,还可以用静态赋值法
pthread_cond_t cond = PTHREAD_COND_INITLALIZER;
[2].等待条件成立
pthread_cond_wait函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量变阻塞,直到条件被信号唤醒。pthread_cond_timedwait用法类似,差别在于 pthread_cond_timedwait函数将阻塞直到条件变量获得信号或者经过由abstime指定的时间,也就是说,如果他在指定时间前条件没有满足,则返回ETLMEOUT,结束等待。
[3].激活
线程被条件变量阻塞后,可被激活。
[4].清除
只有在没有线程等待该条件变量的时候才能清除这个条件变量,否则返回EBUSY。
演示条件变量的使用方法
运行结果
3.异步信号
线程可以接收和处理信号,信号也是一种线程间同步的手段
int pthread_kill(pthread_t threadid, int signo);
int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);
int sigwait(const sigset_t *set, int *sig);
三、出错处理
1.错误码
错误码是一些定义在errno.h中的宏,通常以字母E开头。
2.错误检查
头文件errno.h定义了变量errno,它存储了错误发生时的错误码,通过错误码可以得到错的描述信息。
3.错误的提示信息
1.strerror
#include<string.h>
char *strerror(int errnum);
streeror函数根据参数errnum提供的错误码获取一个描述错误信息的字符串,函数的返回值为指向该字符串的指针,errnum的值通常就是errno。
2.perror
#include<stdio.h>
void perror(const char *message);
perror打印错误信息到stderr。