Chinaunix首页 | 论坛 | 博客
  • 博客访问: 69914
  • 博文数量: 43
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-27 15:04
个人简介

记录,分享

文章分类

全部博文(43)

文章存档

2017年(24)

2015年(1)

2014年(18)

我的朋友

分类: C/C++

2014-06-27 15:40:12

12.3  线程属性

pthread_attr_t结构指定了线程的属性。
可以使用pthread_attr_init将pthread_attr_t初始化为系统默认值。
可以使用pthread_attr_destory回收pthread_attr_init分配的底层资源。

#include
int pthread_attr_init ( pthread_attr_t *attr );
int pthread_attr_destroy ( phtread_attr_t * attr );

分离线程:如果对现有的某个线程的终止状态不感兴趣的话,可以使用pthread_detach让操作系统在线程退出时回收它所占用的资源。

如果在创建线程时就知道不需要了解线程的终止状态,则可以修改 pthread_attr_t 结构中的 detachstate 线程属性,让线程以分离状态启动。
可以使用pthread_attr_setdetachstate函数把线程属性改成两个合法值之一: a.PTHREAD_CREATE_DETACHED 以分离状态启动线程,b.
PTHREAD_CREATE_JOINABLE 正常启动线程

#include
int pthread_attr_getdetachstate ( const pthread_attr_t *restrict attr , int *detachstate );
int pthread_attr_setdetachstate ( phtread_attr_t *attr , int detachstate);

-----------------------------------------------------------------------------------
12.4  同步属性

1.互斥量属性

#include
int pthread_mutex_attr_init ( pthread_mutexattr_t *attr );  //初始化为系统默认值
int pthread_mutex_attr_destroy ( pthread_mutexattr_t *attr );

值得注意的两个属性:
进程共享属性 —— 存在这样的机制,允许相互独立的多个进程把同一个内存区域映射到各自独立的地址空间中,共享读取这片内存。如果互斥量的进程共享属性设为PTHREAD_PROCESS_SHARED,
从多个进程共享的内存区域中分配的互斥量就可以用于这些进程的同步。
类型属性 —— 控制着互斥量的类型。POSIX 定义了四种互斥量类型:a. PTHREAD_MUTEX_NORMAL ; b. PTHREAD_MUTEX_ERRORCHECK c. PTHREAD_MUTEX_RECURSIVE d.
PTHREAD_MUTEX_DEFAULT。 其中PTHREAD_MUTEX_RECURSIVE互斥量类型允许同一线程在互斥量解锁前对该互斥量进行多次加锁。用一个递归互斥量维护锁的计数,在解锁的次数和枷锁次数
不相同的情况下不会释放锁。

#include

int pthread_mutexattr_getpshared ( const pthread_mutexattr_t *restrict attr , int *restrict pshared );
int pthread_mutexattr_setpshared ( pthread_mutexattr_t *attr , int pshared );

int pthread_mutexattr_gettype ( const pthread_mutexattr_t *restrict attr , int *restrict type );
int pthread_mutexattr_settype ( const pthread_mutexattr_t *attr , int type );

2.读写锁属性
 
#include
int pthread_rwlockattr_init ( pthread_rwlockattr_t *attr );
int pthread_rwlockattr_destroy ( pthread_rwlockattr_t *attr );

读写锁支持的唯一属性是 进程共享 属性。

int pthread_rwlockattr_getpshared ( const pthread_rwlockattr_t *restrict attr , int *restrict pshared );
int pthread_rwlockattr_setpshared ( pthread_rwlockattr_t *attr , int pshared );

3.条件变量属性

条件变量支持进程共享属性 (略)


--------------------------------------------------------------------------------------
12.5  重入

线程安全:如果一个函数能在同一时刻被多个线程安全地调用,就称该函数是线程安全的。

操作系统实现支持线程安全函数这一特性时,对posix.1中的一些非线程安全函数,它会提供可替代的线程安全版本。

重要区别 : 如果一个函数对多个线程来说是可重入的,则说这个函数是线程安全的,但这并不能说明对信号处理函数来说该函数也是可重入的。如果函数对异步信号处理函数的重入是安全的,
那么就可以说函数是 异步——信号安全 的。


POSIX提供了以线程安全的方式管理FILE对象的方法。 可以使用 flockfile 和 ftrylockfile 获取与给定FILE对象关联的锁。这个锁是递归的,当占有这把锁的时候,还可以再次获取
该锁,但这份不会导致死锁。虽然这种锁的具体实现并无规定,但要求所有操作FILE对象的标准IO函数表现得就像他们内部调用了flockfile和funlockfile一样。

#include
int ftrylockfile ( FILE *fp );
void flockfile ( FILE *fp );
void funlockfile ( FILE *fp );

为了避免每做一次字符的IO都要加锁解锁,造成严重性能问题,POSIX提供了不加锁版本的基于字符的标准IO函数

#include
int getchar_unlocked(void);
int getc_unlocked(FILE *fp);
int putchar_unlocked(int c);
int putc_unlocked(int c , FILE *fp);

-----------------------------------------------------------------------------------------
12.6  线程私有数据

在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。

#include
int pthread_key_create( pthread_key_t *keyp , void (*destructor)(void *) );

每个线程将keyp对应内存单元存放的键与私有数据地址进行关联,并同时将键与一个析构函数相关联,当线程退出时,如果私有数据地址已被置为非NULL值,则析构函数就会被调用,参数就是
私有数据地址。
当线程调用pthread_exit 或者 线程执行返回,正常退出时,析构函数就会被调用,但如果线程调用了exit,_exit,_Exit,abort或者出现其他非正常退出时,就不会调用析构函数。


#include
int pthread_key_delete ( pthread_key_t *key);

可以通过pthread_key_delete来取消键与线程私有数据的关联。调用pthread_key_delete并不会激活与键关联的析构函数。

#include
void *pthread_getspecific ( pthread_key_t key );
int pthread_setspecific( phtread_key_t key , const void *value);

键一旦创建,就可以通过调用pthread_setspecific函数把键和线程私有数据关联起来。可以通过pthread_getspecific函数获得线程私有数据的地址。


------------------------------------------------------------------------------------------
12.8  线程和信号

#include
int pthread_sigmask( int how , const sigset_t *restrict set , sigset_t *restrict oset );  //屏蔽、恢复信号屏蔽字的多线程版本
pthread_sigmask 工作在线程中,失败时返回错误码,而sigprocmask函数失败时设置errno并返回-1。

int sigwait ( const sigset_t *restrict set , int *restrict signop );
set参数指出了线程等待的信号集,signop指向的整数作为返回值,表明发送信号的编号。

使用sigwait的好处在于可以简化多线程的信号处理。为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用的线程进行信号处理。这些专用线程可以进行函数调用,而
不需要担心调用哪些函数是安全的。因为这些调用来自正常的线程环境,而不是传统的信号处理程序,传统信号处理程序会中断线程的正常执行。


要把信号发送到进程,可以用kill,要把信号发送到线程,可以用pthread_kill
int pthread_kill ( pthread_t pid , int signo );

如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。

闹钟定时器是进程资源,无法被多线程共享地使用。

--------------------------------------------------------------------------------------------
12.9  线程和fork

    子进程通过继承整个地址空间的副本,也就从父进程那里继承了所有的互斥量,读写锁和条件变量的状态。如果父进程包含多个线程,子进程在fork返回以后,如果紧接着不是马上调用exec的话,
就需要清理锁状态。
    在子进程内部只存在一个线程,它是由父进程中调用fork的线程的副本构成的。如果父进程中的线程占有锁,子进程同样占有这些锁。但子进程并不包含占有锁的线程的副本,它无法知道自己占有
了哪些锁并且应该释放哪些锁。
    如果子进程从fork返回后马上调用某个exec函数,就可以避免这样的问题。这种情况下,老的地址空间被丢弃,所以锁的状态无关紧要。
    要清除锁状态,可以通过调用pthread_atfork函数建立fork处理程序。
    
#include
int pthread_atfork ( void (*prepare)(void) , void (*parent)(void) , void (*child)(void) );

prepare 由父进程在fork创建子进程之前调用,任务是获取父进程定义的所有锁。
parent 是在fork创建了子进程以后,但在fork返回之前在父进程环境中调用的,任务是对prepare fork处理程序获得的所有锁进行解锁。
child 是在fork返回之前在子进程中调用的,child fork处理程序必须释放prepare fork处理程序所获得的所有锁。

注意,由于子进程初始时获得的是父进程锁的副本,所以parent 和 child 必须分别对锁进行释放,而不会出现对同一个锁加锁一次解锁两次的情况。
                                                                  
阅读(507) | 评论(0) | 转发(0) |
0

上一篇:ch11_thread

下一篇:ch14_advanced_IO

给主人留下些什么吧!~~