您的位置:首页 > 运维架构 > Linux

linux多线程-线程属性

2015-10-25 01:49 471 查看
所有的函数都是成功返回0, 错误返回错误编号

通常管理这些属性的函数都遵循相同的模式:

1, 每个对象与它自己类型的属性进行关联(线程与线程属性,互斥量与互斥量属性关联,等等),属性对象对应用程序来说是不透明的,应用程序不需要了解有关属性对象内部结构的详细细节,增强了应用程序的可移植性。所以需要相应的函数来管理相应属性和相应的属性对象(set, get)

2, 每个属性都有初始化函数和销毁函数,初始化函数,可把属性设置为默认值

3,销毁属性的函数

4,每个属性都有一个从属性对象中获取值的函数。(get)

5,每个属性都有一个设置值的函数。(set)

线程属性:

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);


线程支持的属性:分离状态属性,线程栈末尾的警戒缓冲区大小(字节数),线程栈的最低地址,线程栈的最小长度(字节数)

分离线程概念:对某个线程的终止状态不感兴趣的话,可以把线程设置为分离状态。PTHREAD_CREATE_DETACHED以分离状态启动线程,或PTHREAD_CREATE_JOINABLE(默认),正常启动线程。

#include <pthread.h>

int pthread_attr_getdeachstate(const pthread_attr_t *restrict attire, int *deachstate);

int pthread_attr_setdeachstate(pthread_attr_t *attr, int *deachstate);

线程栈属性:

pthread_attr_getstack  pthread_attr_setstack

设置线程栈的大小

pthread_attr_getstacksize  pthread_attr_setstacksize 

线程属性guard size控制线程栈末尾,避免栈溢出

pthread_attr_getguardsize pthread_attr_setguardsize 

互斥量属性

#include<pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr); //PTHREAD_MUTEX_INITIALIZER常量初始化
int pthread_mutexattr_destory(pthread_mutexattr_t *attr);
//属性的初始化函数与删除属性

3个属性:进程共享属性, 健壮属性,以及类型属性。

#include<pthread.h>
int pthread_mutexattr_getshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);

int pthread_mutexattr_setshared(pthread_mutexattr_t *attr, int pshared);
共享属性(PTHREAD_PROCESS_SHARED),其他的进程,可以访问到这个线程。 PTHREAD_PROCESS_PRIVATE(默认),只有该进程内可以共享。

互斥量的健壮性

#include<pthread.h>

int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict attr, int *restrict robust);
int pthread_mutexattr_setrobust(pthread_mutexattr_t, *attr, int robust);


PTHREAD_MUTEX_ROBUST设置为健壮性,以为着当互斥量的进程终止时,需要解决互斥量的状态恢复问题。这种情况发生时,互斥量处于锁定状态,恢复起来很困难。其他阻塞在这个锁的进程将会一直阻塞下去。

PTHREAD_MUTEX_STALLED,意味着持有互斥量的进程终止时不需要采取特别的动作。

使用健壮性互斥量改变了我们使用pthread_mutex_lock的方式,现在必须检查3个返回值而不是之前的两个:不需要恢复的成功、需要恢复的成功以及失败。但是不用健壮性的互斥量,也可以只检查成功或失败。

如果应用状态无法恢复,线程对互斥量解锁以后,该互斥量处于永久不可用状态。为避免这样问题,线程调用pthread_mutex_consistent函数,指明与该互斥量解锁之前是一致的。如果线程没有先调用pthread_mutex_consistent对互斥量进行解锁,那么其他获取该互斥量的阻塞线程就会得到 错误码 ENOTERCOVERRABLE,这种情况发生,互斥量将不可用,线程提前调用下面函数,能让互斥量正常工作,这样他就可被持续使用。

#include <pthread.h>
int pthread_mutex_consistent(pthread_mutex_t *mutex);

类型互斥量属性:控制着互斥量锁的特性



通过以下函数获取或修改互斥量的类型属性

#include<pthread.h>

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


改变条件时必须占有互斥量,使用递归互斥量就不是一个好主意。如果互斥量被多次加锁,然后再调用pthread_cond_wait函数中,条件永远都得不到满足,因为pthread_cond_wait所做的解锁操作不能释放互斥量。使用递归锁可能很难处理,因此应该只在没有其他可行方案的时候才使用他们。

读写锁的属性

#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destory(pthread_rwlockattr_t *attr);
读写锁唯一支持属性是进程共享属性。与互斥量进程共享属性相同。

#include<pthread.h>
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t * restrict attr, int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

条件变量属性:进程共享属性和时钟属性

初始化与销毁函数

#include <pthread.h>
int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destory(pthread_condattr_t *attr);
设置共享属性函数
#include <pthread.h>

int pthread_condattr_getpshared(const pthread_condattr *restrict, int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared);
时钟属性控制计算pthread_cod_timewait函数的超时函数(tspr)采用的是那个时钟。

#include <pthread.h>
int pthread_condattr_getlock(const pthread_condattr_t *restrict attr, clocked_t *restrict clock_id);
int pthread_condattr_setlock(pthread_condattr_t *attr, clocked_t clock_id);

屏障属性:进程共享属性

#include<pthread.h>
int pthread_barrierattr_init(pthread_barrierattr_t *attr);
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);

#include <pthread.h>
int pthread_barrierattr_getshared(const pthread_barrierattr_t *restrict attr, int *restrict pshared);
int pthread_barrierattr_setpshared(pthread_barrierattr *attr, int shared);


重入

如果一个函数(库函数或自定义)在相同的时间点,可以被多个线程安全的调用,称为该函数为线程安全。

对于一些非线程安全的函数,系统会有线程安全的版本,他们与非线程安全的版本名字类似,只不过在名字的最后加了_r。

线程的特定数据

线程私有数据(特定数据),是存储和查询某个线程相关的数据的一种机制。希望每个线程可以独立地访问数据副本,而不需要担心与其他线程的同步访问,问题。

一个进程中所有线程都可以访问这个进程的整个地址空间,除使用寄存器以外,一个线程没有办法阻止另一个线程访问他的数据。线程的特定数据也不例外,虽然底层的实现部分并不能阻止这种访问能力,但管理线程特定数据的函数可以提高线程间的数据独立性。

分配线程特定数据之前,需要创建与改数据关联的键。这个键将用于对线程特定数据的访问。

#include <pthread.h>
int pthread_key_create(pthread_key_t *keyp, void (*destructor) (void *));创建的键存储在keyp指向的内存单元中,这个键可以被进程中所有线程使用,但每个线程把这个键与不同的线程特定数据地址进行关联。
1 pthread_key_create可以为该键关联一个可选择的析构函数。 当这个线程退出时,如果数据地址已经被置为非空值,那么析构函数就会被调用。

2 线程调用pthread_exit或者线程执行返回,正常退出,析构函数就会被调用。如果线程调用了exit, _exit, _Exit或者abort,或出现其他非正常退出的时候,就不会调用析构函数。

3 线程通常使用malloc为线程特定数据非配内存,析构函数通常可写释放已分配的内存。

4 线程可以为线程特定数据分配多个键,每个键都可以有一个析构函数与它关联。每个键的析构函数可以互不相同,当然也可以相同。

5 线程特定数据的析构函数将按照操作系统实现定义的顺序被调用

pthread_key_delete来取消键与线程特定数据值之间的关联。

#include <pthread.h>
int pthread_key_delete(pthread_key_t key);调用pthread_key_delete并不会激活与键关联的析构函数

#include <pthread.h>

pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void (*initfn) (void));
initflag必须是一个非本地的变量(如全局变量或静态变量),而且必须初始化为PTHREAD_ONCE_INIT

如果每个线程都调用pthread_once,系统就能保证初始化例程initfn只被调用一次,既系统首次调用pthread_once时。创建键的时候,避免冲突的一个正确方法就是在,pthread_once内部调用 pthread_key_create

#include <pthread.h>
void *pthread_getspecific(pthread_key_t key);

int pthread_setspecific(pthread_key_t, const void *value);键一旦创建以后,就可以通过调用pthread_setspecific函数把键和线程特定数据关联起来。
可通过pthread_getspecific函数获得线程特定数据的地址。如果没有关联,将返回一个空指针。可利用这个值来看是否需要调用set

取消选项

有两个线程属性并没有包含在pthread_attr_t结构中,他们是可取消状态和可取消类型的。这两个属性影响着线程在响应pthread_cancel函数调用时所呈现的行为。

线程与信号

在线程内部控制信号的接收与阻止,有几个函数

线程和fork

线程调用fork时,就为了子进程创建整个进程地址空间的副本,子进程与父进程是完全不同的进程,只要两者没有对内容做出改动,父进程和子进程之间还可以共享内存页的副本。有一些锁的状态需要清理,有几个函数。

线程和IO

pread 和 pwrite函数,这些函数在多线程环境下是非常有用的,因为进程中所有线程共享相同的文件描述符。

考虑两个线程,在同一时间对同一文件描述符进行读写操作。

线程A 线程B

lseek(fd, 300, SEEK_SET); lseek(fd, 700, SEEK_SET) 

read(fd, buf1, 100); read(fd, buf2, 100)

如果线程A执行lseek然后线程B在线程Aread之前调用lseek,那么两个线程最终会读取同一条记录。很显然我们不想这样。

为了这个问题,可以使用pread,使偏移量的设定和数据的读取成为一个原子操作。

线程A 线程B

pread(fd, buffé, 100, 300)  pread(fd, buffé, 100, 700)

使用pread可以确保线程A读取偏移量为300的记录,而线程B读取偏移量为700的记录。

可以使用write来解决并发线程对同一文件进行写操作的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 多线程 线程