本帖最后由 look_w 于 2017-9-23 15:44 编辑
5、线程私有数据进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有。但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-specific Date)TSD来解决。在线程内部,私有数据可以被各个函数访问,但对其他线程是屏蔽的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。
线程私有数据采用了一键多值的技术,即一个键对应多个数值,访问数据时好像是对同一个变量进行访问,但其实是在访问不同的数据。
创建私有数据的函数有4个:pthread_key_create(创建), pthread_setspecific(设置), pthread_getspecific(获取), pthread_key_delete(删除)。
1 #include <pthread.h>
2 int pthread_key_creadte(pthread_key_t *key,void (*destr_fuction) (void *));
3 int pthread_setspecific(pthread_key_t key,const void * pointer));
4 void * pthread_getspecific(pthread_key_tkey);
5 int pthread_key_delete(ptherad_key_t key);
具体用法如下:
1.创建一个类型为pthread_key_t类型的变量。
2.调用pthread_key_create()来创建该变量。该函数有两个参数,第一个参数就是上面声明的pthread_key_t变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成NULL,这样系统将调用默认的清理函数。
3.当线程中需要存储特殊值的时候,可以调用pthread_setspcific()。该函数有两个参数,第一个为前面声明的pthread_key_t变量,第二个为void*变量,这样你可以存储任何类型的值。
4.如果需要取出所存储的值,调用pthread_getspecific()。该函数的参数为前面提到的pthread_key_t变量,该函数返回void *类型的值。
6、线程同步线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和异步信号。
1)互斥锁(mutex)通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。
1
int
pthread_mutex_init(pthread_mutex_t *mutex,const
pthread_mutex_attr_t *mutexattr);
2
int
pthread_mutex_lock(pthread_mutex *mutex);
3
int
pthread_mutex_destroy(pthread_mutex *mutex);
4
int
pthread_mutex_unlock(pthread_mutex *
(1)先初始化锁init()或静态赋值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER
PTHREAD_MUTEX_TIMED_NP:其余线程等待队列
PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许线程多次加锁,不同线程,解锁后重新竞争
PTHREAD_MUTEX_ERRORCHECK_NP:检错,与一同,线程请求已用锁,返回EDEADLK;
PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,解锁后重新竞争
(2)加锁,lock,trylock,lock阻塞等待锁,trylock立即返回EBUSY
(3)解锁,unlock需满足是加锁状态,且由加锁线程解锁
(4)清除锁,destroy(此时锁必需unlock,否则返回EBUSY,//Linux下互斥锁不占用资源内存
2)条件变量(cond)利用线程间共享的全局变量进行同步的一种机制。
1
int
pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
2
int
pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
3
int
pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const
timespec *abstime);
4
int
pthread_cond_destroy(pthread_cond_t *cond);
5
int
pthread_cond_signal(pthread_cond_t *cond);
6
int
pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
(1)初始化.init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL
(2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真
timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
(3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
(4)清除条件变量:destroy;无线程等待,否则返回EBUSY
对于intpthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
intpthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex, const struct timespec *abstime);
一定要在mutex的锁定区域内使用。
如果要正确的使用pthread_mutex_lock与pthread_mutex_unlock,请参考
pthread_cleanup_push和pthread_cleanup_pop宏,它能够在线程被cancel的时候正确的释放mutex!
另外,posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是所,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/
- void *thread1(void *);
- void *thread2(void *);
- int i=1;
- int main(void)
- {
- pthread_t t_a;
- pthread_t t_b;
- pthread_create(&t_a,NULL,thread1,(void *)NULL);/*创建线程t_a*/
- pthread_create(&t_b,NULL,thread2,(void *)NULL); /*创建线程t_b*/
- pthread_join(t_a, NULL);/*等待线程t_a结束*/
- pthread_join(t_b, NULL);/*等待线程t_b结束*/
- pthread_mutex_destroy(&mutex);
- pthread_cond_destroy(&cond);
- exit(0);
- }
- void *thread1(void *junk)
- {
- for(i=1;i<=6;i++)
- {
- printf("thread1 i=%d\n",i);
- pthread_mutex_lock(&mutex);/*锁住互斥量*/
- printf("thread1: lock %d\n", __LINE__);
- if(i%3==0){
- printf("thread1:signal 1 %d\n", __LINE__);
- pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b线程*/
- printf("thread1:signal 2 %d\n", __LINE__);
- sleep(1);
- }
- pthread_mutex_unlock(&mutex);/*解锁互斥量*/
- printf("thread1: unlock %d\n\n", __LINE__);
- sleep(1);
- }
- }
- void *thread2(void *junk)
- {
- while(i<6)
- {
- printf("thread2 i=%d\n",i);
- pthread_mutex_lock(&mutex);
- printf("thread2: lock %d\n", __LINE__);
- if(i%3!=0){
- printf("thread2: wait 1 %d\n", __LINE__);
- pthread_cond_wait(&cond,&mutex);/*解锁mutex,并等待cond改变*/
- printf("thread2: wait 2 %d\n", __LINE__);
- }
- pthread_mutex_unlock(&mutex);
- printf("thread2: unlock %d\n\n", __LINE__);
- sleep(1);
- }
- }
3)信号量如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。
#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
两个原子操作函数:
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post:给信号量的值加1;
sem_wait:给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。
intsem_destroy(sem_t *sem);
这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源。 |