- 浏览: 1653307 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (405)
- C/C++ (16)
- Linux (60)
- Algorithm (41)
- ACM (8)
- Ruby (39)
- Ruby on Rails (6)
- FP (2)
- Java SE (39)
- Java EE (6)
- Spring (11)
- Hibernate (1)
- Struts (1)
- Ajax (5)
- php (2)
- Data/Web Mining (20)
- Search Engine (19)
- NLP (2)
- Machine Learning (23)
- R (0)
- Database (10)
- Data Structure (6)
- Design Pattern (16)
- Hadoop (2)
- Browser (0)
- Firefox plugin/XPCOM (8)
- Eclise development (5)
- Architecture (1)
- Server (1)
- Cache (6)
- Code Generation (3)
- Open Source Tool (5)
- Develope Tools (5)
- 读书笔记 (7)
- 备忘 (4)
- 情感 (4)
- Others (20)
- python (0)
最新评论
-
532870393:
请问下,这本书是基于Hadoop1还是Hadoop2?
Hadoop in Action简单笔记(一) -
dongbiying:
不懂呀。。
十大常用数据结构 -
bing_it:
...
使用Spring MVC HandlerExceptionResolver处理异常 -
一别梦心:
按照上面的执行,文件确实是更新了,但是还是找不到kernel, ...
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法 -
dsjt:
楼主spring 什么版本,我的3.1 ,xml中配置 < ...
使用Spring MVC HandlerExceptionResolver处理异常
线程2
线程1中我们介绍了线程和线程同步,本部分将学习线程控制的细节。我们将要看到线程属性、同步原语属性,线程私有数据。
1、线程限制:
Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询。和其它的限制使用目的一样,为了应用程序的在不同操作
系统的可移植性。
一些限制:
PTHREAD_DESTRUCTOR_ITERATIONS: 销毁一个线程数据最大的尝试次数,可以通过_SC_THREAD_DESTRUCTOR_ITERATIONS作为sysconf的参数查询。
PTHREAD_KEYS_MAX: 一个进程可以创建的最大key的数量。可以通过_SC_THREAD_KEYS_MAX参数查询。
PTHREAD_STACK_MIN: 线程可以使用的最小的栈空间大小。可以通过_SC_THREAD_STACK_MIN参数查询。
PTHREAD_THREADS_MAX:一个进程可以创建的最大的线程数。可以通过_SC_THREAD_THREADS_MAX参数查询
2、线程属性:
我们在调用pthread_create,线程属性参数传递了NULL值,创建的线程使用默认的线程属性。我们如果想修改默认的线程属性,可以使用pthread_attr_init
初始化pthread_attr_t结构,然后通过pthread_setxxx来修改这个结构,最后将它作为创建线程的参数,与线程关联。
我们使用完了线程属性之后可以通过pthread_attr_destroy来销毁。
pthread_attr_destroy会销毁有pthread_attr_init动态申请的内存。
pthread_attr_t结构对应用程序来说是透明的,应用程序不知道内部的结构,只能通过其他的方法来查询和设置每个属性,这和面向对象的封装性一样,有助于
提高程序的移植性。
线程定义了以下属性:
detachstate: detached线程的属性。
guardsize:线程栈后保护区的大小。
stackaddr:线程栈的最低地址。
stacksize:线程栈的大小。
我们可以通过pthread_detach来让线程在退出的时候,让操作系统回收其资源。但是如果我们不需要知道线程退出的状态,我们可以在一开始创建线程的时候就
将其设置为detach状态,这个可以通过pthread_attr_setdetachstate修改detachstate来实现,可以设置两个值:PTHREAD_CREATE_DETACHED、
PTHREAD_CREATE_JOINABLE,默认是正常的PTHREAD_CREATE_JOINABLE。
我们可以通过pthread_attr_getdetachstate来获得当前的detachstate,通过pthread_attr_setdetachstate来设置detachstate为:PTHREAD_CREATE_DETACHED和
PTHREAD_CREATE_JOINABLE。
例子:
创建一个detached状态的线程:
我们忽略的pthread_attr_destory的返回值,因为它不会失败,即使失败我们也很难去进行清理,因为这是pthread_attr_destory是执行清理的接口。
支持线程stack属性是posix标准可选项,但是是XSI的必选。可以在编译的时候通过测试_POSIX_THREAD_ATTR_STACKADDR和_POSIX_THREAD_ATTR_STACKSIZE
宏来检查是否支持线程堆栈属性,也可以在运行时通过sysconf检查_SC_THREAD_ATTR_STACKADDR和_SC_THREAD_ATTR_STACKSIZE
POSIX.1定义了一些接口来操作线程堆栈属性,两个老的:pthread_attr_getstackaddr和pthread_attr_setstackaddr由于具有歧义,已经被Single UNIX标准
废弃。可以通过新的函数pthread_attr_getstack和pthread_attr_setstack来做。
这两个函数既可以操作堆栈的地址也可以操作堆栈的大小。
进程不需要担心堆栈的大小,但是使用线程时,应该小心,因为进程的虚拟地址空间被多个线程堆栈共享,如果你使用非常多的线程,你需要减少线程栈的大小,
而如果线程申请了大量的自动变量或者调用函数使用了栈帧过深(比如深层递归),那么需要增大单个线程的栈大小。
你可以使用malloc或者mmap来申请空间,然后使用pthread_attr_setstack指令另一个栈空间,stackaddr必须是栈区的最低地址,不一定是栈首,因为硬件的结构
栈地址增长可能是从低到高还是相反,这也是pthread_attr_getstackaddr具有歧义的原因。
可以使用pthread_attr_getstacksize和pthread_attr_setstacksize来获得和设置线程属性stacksize。
如果你不想自己申请空间,你可以使用pthread_attr_setstacksize来设置栈大小。
guardsize是栈区末尾一端空间的大小,它用来防止栈溢出。默认的大小是PAGESIZE字节,我们可以把guardsize设置为0来禁止
这个特性。如果我们改变了stackaddr,系统假设我们将要自己维护栈空间并禁止掉栈防卫缓冲。
guardsize应设置为页大小的整数倍,如果栈指针溢出到防卫区域,应用程序将收到错误,有可能是信号。
其他的线程属性:
线程还有其他属性,但是不是由pthread_attr_t类型表示的:
1)是否可以取消状态
2)取消类型
3)并发级别
1-2见取消选项一节
并发级别控制了用户级线程和内核级线程之间的映射关系。如果实现上二者是一对一的关系,那么改变并发级别将不起作用。如果实现上是多个用户级别的线程
映射到一个内核级别的线程,可以使用pthread_setconcurrency来向操作系统提供一个hint,让其满足期望的并发度。
pthread_setconcurrency返回当前的并发级别,如果操作系统控制并发级别(比如没有调用过pthread_setconcurrency)。应用程序可以通过把level设置为0,来
撤销上一次调用。
3、同步属性
像线程属性一样,同步对象互斥量、读写锁、条件变量,也有他们的属性。
1)互斥量属性:
pthread_mutexattr_init初始化pthread_mutexattr_t结构,pthread_mutexattr_destroy销毁该结构。
进程共享属性和类型属性是我们感兴趣的两个互斥量属性。进程共享属性是POSIX可选的属性,可以通过测试_POSIX_THREAD_PROCESS_SHARED是否定义来检测是否支持。
也可以在运行时传_SC_THREAD_PROCESS_SHARED到sysconf来测试。Single Unix规范则要求支持此选项。
共享属性可设为两个值:PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED两个值。默认是PTHREAD_PROCESS_PRIVATE,进程及进程内线程只能访问本进程内的互斥量。
PTHREAD_PROCESS_SHARED多个进程可以共享此互斥量。
可以通过pthread_mutexattr_getshared和pthread_mutexattr_setshared来查询和设置进程共享属性:
类型属性:
POSIX.1定义了四种互斥量类型属性:
PTHREAD_MUTEX_NORMAL:不进行特殊的错误检查和死锁检测
PTHREAD_MUTEX_ERRORCHECK:进行错误检查
PTHREAD_MUTEX_RECURSIVE:允许同一线程在未释放锁之前多次再加锁
PTHREAD_MUTEX_DEFAULT:依赖于实现,具体映射到以上三种中的一种
可以通过pthread_mutexattr_gettype和pthread_mutexattr_settype来查询和修改type属性:
递归互斥量主要用处是:现有的单线程结构应用到多线程环境中,同时为了兼容不能改变接口。
2)读写锁属性:
和互斥量一样,读写锁也使用pthread_rwlockattr_init和pthread_rwlockattr_destroy两个方法来创建和销毁读写锁属性。
读写锁唯一支持的属性有进程共享属性,这个和互斥量类似:
3)条件变量属性:
条件变量也有一对初始化和销毁的方法:
和其他的同步原语一样,支持进程共享属性:
4、可重入性:
我们已经在信号处理那块讨论了可重入函数。对于可重入来说,线程和信号处理函数类似。一个函数如果可以安全的被多个线程同时调用,
则称之为线程安全的。如果一个函数可重入,那么它是线程安全的,但使用了非可重入函数(例如malloc(3))的线程也能通过互斥等同步
机制实现线程安全。所以不能从线程安全函数是可重入函数的结果,特别是线程安全并不能保证异步信号安全。
POSIX还提供了以线程安全的方式访问FILE对象的方法,即针对FILE对象的锁同步机制:
标准I/O例程需要他们自己的锁。但如果我们在每次一个字符的I/O操作使用锁会产生严重的性能问题。为了避免上述问题,为基于字符的标准I/O,
提供了一下四个unlock版本。
这四个函数需要在flockfile或者ftrylockfile内部使用。否则会有不可预测的结果。
线程安全的getenv例子:
5、线程私有数据
线程私有数据是用来存储和查找与线程相关数据的一种机制。之所以叫私有数据,是因为每一个线程都访问它自己的那份数据的拷贝,不需要考虑
和其他线程同步访问数据。
使用它的原因:
1、有时候我们需要维护每个线程一份数据。虽然我们可以使用线程id作为可以的hash来做,但是还需要互斥访问和保护,防止其他线程访问该线程
的私有数据。
2、提供了将基于进程的结构适配成多线程环境的方法。比如errno
在申请和线程相关的数据之前,我们需要创建key,来和这个数据关联,我们以后会使用这个key来访问这个数据。
多个线程可以使用同一个key,但是每个线程要关联不同的该线程私有的数据地址。当线程退出时(pthread_exit,return),数据的地址被设置为NULL,
参数二指定的destructor将会被调用,并将该私有数据地址作为参数传递到该函数中。
但是如果线程调用的是exit,_exit,_Exit后者abort或者其他不正常退出方法,destructor不会被调用。线程经常使用malloc申请线程私有数据,这个
destructor用于释放该数据申请的内存。
一个线程可以用多个key来关联线程的私有数据,可以使用相同的destructor,也可以不同。
如果我们想将该key和关联的私有数据断开,那么可以通过调用pthread_key_delete来完成:
调用这个行数不会触发上面我们说的destructor的调用,我们需要采取其他的措施来释放。
我们需要保证key不被改变。
我们可能使用下面的错误的代码来做初始化,以保证不被多次初始化:
这个由于线程条件竞争,可能被初始化多次或者看到不一致的状态。使用pthread_once可以解决这个问题:
参数initflag必须是非局部变量,并且被初始化为PTHREAD_ONCE_INIT。
pthread_once可以保证初始化例程initfn只被调用一次:
一旦key被创建,我们就可以通过调用pthread_setspecific将key和私有数据关联,可以通过调用pthread_getspecific来通过key获得
关联的数据。
我们前面通过改变getenv_r来改造了一个线程安全的获得环境变量的函数,如果我们不能够修改函数接口,那该如何去做?我们可以通过
使用线程私有数据来维护每个线程一个数据buffer的拷贝。
例子:
6、取消选项
两个属性没有包含在pthread_attr_t结构中:可取消状态和取消类型。取消状态可以取:PTHRAD_CANCEL_ENABLE和PTHREAD_CANCEL_DISABLE。
可以通过调用pthread_setcancelstate:
在一个原子操作中,将当前可取消状态设置成state,并将以前的状态设置到oldstate。
默认pthread_cancel并不使线程立即停止,线程会继续运行到取消点,一个取消点是线程检查是否被取消的地方。一个线程在调用一下函数时,
会检查是否有退出请求:
accept、mq_timedsend、putpmsg、sigsuspend、aio_suspend、msgrcv、pwrite、sigtimedwait、clock_nanosleep、msgsnd、read
sigwait、close、msync、readv、sigwaitinfo、connect、nanosleep、recv、sleep 、creat、open、recvfrom、system 、fcntl2
pause、recvmsg、tcdrain 、fsync、poll、select、usleep、getmsg、pread、sem_timedwait、wait、getpmsg、pthread_cond_timedwait
sem_wait、waitid 、lockf、pthread_cond_wait、send、waitpid、mq_receive、pthread_join、sendmsg、write 、mq_send
pthread_testcancel、sendto、writev 、mq_timedreceive、putmsg、sigpause
如果应用程序很长时间没有没有调用上面所列的函数,那么可以通过pthread_testcancel在程序中添加自己的取消点。
我们可以通过pthread_setcanceltype来改变取消的类型,而不是保持上面说的默认的行为:
type可以被设置为:PTHREAD_CANCEL_DEFERRED或者PTHREAD_CANCEL_ASYNCHRONOUS.返回以前的type,设置到oldtype中。
异步取消PTHREAD_CANCEL_ASYNCHRONOUS不同于PTHREAD_CANCEL_DEFERRED,可以在任何时候被取消,没有必要到达一个取消点才被取消。
线程1中我们介绍了线程和线程同步,本部分将学习线程控制的细节。我们将要看到线程属性、同步原语属性,线程私有数据。
1、线程限制:
Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询。和其它的限制使用目的一样,为了应用程序的在不同操作
系统的可移植性。
一些限制:
PTHREAD_DESTRUCTOR_ITERATIONS: 销毁一个线程数据最大的尝试次数,可以通过_SC_THREAD_DESTRUCTOR_ITERATIONS作为sysconf的参数查询。
PTHREAD_KEYS_MAX: 一个进程可以创建的最大key的数量。可以通过_SC_THREAD_KEYS_MAX参数查询。
PTHREAD_STACK_MIN: 线程可以使用的最小的栈空间大小。可以通过_SC_THREAD_STACK_MIN参数查询。
PTHREAD_THREADS_MAX:一个进程可以创建的最大的线程数。可以通过_SC_THREAD_THREADS_MAX参数查询
2、线程属性:
我们在调用pthread_create,线程属性参数传递了NULL值,创建的线程使用默认的线程属性。我们如果想修改默认的线程属性,可以使用pthread_attr_init
初始化pthread_attr_t结构,然后通过pthread_setxxx来修改这个结构,最后将它作为创建线程的参数,与线程关联。
我们使用完了线程属性之后可以通过pthread_attr_destroy来销毁。
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr);
pthread_attr_destroy会销毁有pthread_attr_init动态申请的内存。
pthread_attr_t结构对应用程序来说是透明的,应用程序不知道内部的结构,只能通过其他的方法来查询和设置每个属性,这和面向对象的封装性一样,有助于
提高程序的移植性。
线程定义了以下属性:
detachstate: detached线程的属性。
guardsize:线程栈后保护区的大小。
stackaddr:线程栈的最低地址。
stacksize:线程栈的大小。
我们可以通过pthread_detach来让线程在退出的时候,让操作系统回收其资源。但是如果我们不需要知道线程退出的状态,我们可以在一开始创建线程的时候就
将其设置为detach状态,这个可以通过pthread_attr_setdetachstate修改detachstate来实现,可以设置两个值:PTHREAD_CREATE_DETACHED、
PTHREAD_CREATE_JOINABLE,默认是正常的PTHREAD_CREATE_JOINABLE。
#include <pthread.h> int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
我们可以通过pthread_attr_getdetachstate来获得当前的detachstate,通过pthread_attr_setdetachstate来设置detachstate为:PTHREAD_CREATE_DETACHED和
PTHREAD_CREATE_JOINABLE。
例子:
创建一个detached状态的线程:
#include <pthread.h> #include <string.h> #include <stdio.h> int make_detached_thread(void *(*fn)(void *), void *arg){ int err; pthread_t tid; pthread_attr_t attr; err = pthread_attr_init(&attr); if(err != 0){ return err; } err = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); if(err == 0) pthread_create(&tid,&attr,fn,arg); pthread_attr_destory(&attr); return err; }
我们忽略的pthread_attr_destory的返回值,因为它不会失败,即使失败我们也很难去进行清理,因为这是pthread_attr_destory是执行清理的接口。
支持线程stack属性是posix标准可选项,但是是XSI的必选。可以在编译的时候通过测试_POSIX_THREAD_ATTR_STACKADDR和_POSIX_THREAD_ATTR_STACKSIZE
宏来检查是否支持线程堆栈属性,也可以在运行时通过sysconf检查_SC_THREAD_ATTR_STACKADDR和_SC_THREAD_ATTR_STACKSIZE
POSIX.1定义了一些接口来操作线程堆栈属性,两个老的:pthread_attr_getstackaddr和pthread_attr_setstackaddr由于具有歧义,已经被Single UNIX标准
废弃。可以通过新的函数pthread_attr_getstack和pthread_attr_setstack来做。
#include <pthread.h> int pthread_attr_getstack(const pthread_attr_t *restrict attr,void ** restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t *stacksize);
这两个函数既可以操作堆栈的地址也可以操作堆栈的大小。
进程不需要担心堆栈的大小,但是使用线程时,应该小心,因为进程的虚拟地址空间被多个线程堆栈共享,如果你使用非常多的线程,你需要减少线程栈的大小,
而如果线程申请了大量的自动变量或者调用函数使用了栈帧过深(比如深层递归),那么需要增大单个线程的栈大小。
你可以使用malloc或者mmap来申请空间,然后使用pthread_attr_setstack指令另一个栈空间,stackaddr必须是栈区的最低地址,不一定是栈首,因为硬件的结构
栈地址增长可能是从低到高还是相反,这也是pthread_attr_getstackaddr具有歧义的原因。
可以使用pthread_attr_getstacksize和pthread_attr_setstacksize来获得和设置线程属性stacksize。
#include <pthread.h> int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
如果你不想自己申请空间,你可以使用pthread_attr_setstacksize来设置栈大小。
guardsize是栈区末尾一端空间的大小,它用来防止栈溢出。默认的大小是PAGESIZE字节,我们可以把guardsize设置为0来禁止
这个特性。如果我们改变了stackaddr,系统假设我们将要自己维护栈空间并禁止掉栈防卫缓冲。
#include <pthread.h> int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize); int pthread_attr_setguardsize(const pthread_attr_t *attr, size_t guardsize);
guardsize应设置为页大小的整数倍,如果栈指针溢出到防卫区域,应用程序将收到错误,有可能是信号。
其他的线程属性:
线程还有其他属性,但是不是由pthread_attr_t类型表示的:
1)是否可以取消状态
2)取消类型
3)并发级别
1-2见取消选项一节
并发级别控制了用户级线程和内核级线程之间的映射关系。如果实现上二者是一对一的关系,那么改变并发级别将不起作用。如果实现上是多个用户级别的线程
映射到一个内核级别的线程,可以使用pthread_setconcurrency来向操作系统提供一个hint,让其满足期望的并发度。
#include <phtread.h> int pthread_getconcurrency(void); int pthread_setconcurrency(int level);
pthread_setconcurrency返回当前的并发级别,如果操作系统控制并发级别(比如没有调用过pthread_setconcurrency)。应用程序可以通过把level设置为0,来
撤销上一次调用。
3、同步属性
像线程属性一样,同步对象互斥量、读写锁、条件变量,也有他们的属性。
1)互斥量属性:
pthread_mutexattr_init初始化pthread_mutexattr_t结构,pthread_mutexattr_destroy销毁该结构。
#include <pthread.h> int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
进程共享属性和类型属性是我们感兴趣的两个互斥量属性。进程共享属性是POSIX可选的属性,可以通过测试_POSIX_THREAD_PROCESS_SHARED是否定义来检测是否支持。
也可以在运行时传_SC_THREAD_PROCESS_SHARED到sysconf来测试。Single Unix规范则要求支持此选项。
共享属性可设为两个值:PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED两个值。默认是PTHREAD_PROCESS_PRIVATE,进程及进程内线程只能访问本进程内的互斥量。
PTHREAD_PROCESS_SHARED多个进程可以共享此互斥量。
可以通过pthread_mutexattr_getshared和pthread_mutexattr_setshared来查询和设置进程共享属性:
#include <pthread.h> int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared); int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
类型属性:
POSIX.1定义了四种互斥量类型属性:
PTHREAD_MUTEX_NORMAL:不进行特殊的错误检查和死锁检测
PTHREAD_MUTEX_ERRORCHECK:进行错误检查
PTHREAD_MUTEX_RECURSIVE:允许同一线程在未释放锁之前多次再加锁
PTHREAD_MUTEX_DEFAULT:依赖于实现,具体映射到以上三种中的一种
可以通过pthread_mutexattr_gettype和pthread_mutexattr_settype来查询和修改type属性:
#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);
递归互斥量主要用处是:现有的单线程结构应用到多线程环境中,同时为了兼容不能改变接口。
2)读写锁属性:
和互斥量一样,读写锁也使用pthread_rwlockattr_init和pthread_rwlockattr_destroy两个方法来创建和销毁读写锁属性。
#include <pthread.h> int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
读写锁唯一支持的属性有进程共享属性,这个和互斥量类似:
#include <pthread.h> int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, int *restrict pshared) int pthread_rwlockattr_setpshared(phtread_rwlockattr_t *attr, int pshared),
3)条件变量属性:
条件变量也有一对初始化和销毁的方法:
#include <pthread.h> int pthread_condattr_init(pthread_condattr_t *attr); int pthread_condattr_destroy(pthread_condattr_t *attr);
和其他的同步原语一样,支持进程共享属性:
#include <pthread.h> int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr, int *restrict pshared); int pthread_condattr_setpshared(pthread_condattr *attr, int pshared);
4、可重入性:
我们已经在信号处理那块讨论了可重入函数。对于可重入来说,线程和信号处理函数类似。一个函数如果可以安全的被多个线程同时调用,
则称之为线程安全的。如果一个函数可重入,那么它是线程安全的,但使用了非可重入函数(例如malloc(3))的线程也能通过互斥等同步
机制实现线程安全。所以不能从线程安全函数是可重入函数的结果,特别是线程安全并不能保证异步信号安全。
POSIX还提供了以线程安全的方式访问FILE对象的方法,即针对FILE对象的锁同步机制:
#include <stdio.h> int ftrylockfile(FILE *fp); void flockfile(FILE *fp); void funlockfile(FILE *fp);
标准I/O例程需要他们自己的锁。但如果我们在每次一个字符的I/O操作使用锁会产生严重的性能问题。为了避免上述问题,为基于字符的标准I/O,
提供了一下四个unlock版本。
#include <stdio.h> int getchar_unlocked(void); int getc_unklocked(void); int putchar_unlocked(void); int putc_unlocked(void);
这四个函数需要在flockfile或者ftrylockfile内部使用。否则会有不可预测的结果。
线程安全的getenv例子:
#include <string.h> #include <errno.h> #include <pthread.h> #include <stdlib.h> extern char **environ; pthread_mutex_t env_mutex; static pthread_once_t init_done = PTHREAD_ONCE_INIT; static void thread_init(void){ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&env_mutex,&attr); pthread_mutex_attr_destroy(&attr); } int getenv_r(const char *name, char *buf, int buflen){ int i, len, olen; pthread_once(&init_done,thread_init); len = strlen(name); pthread_mutex_lock(&env_mutex); for(i = 0; environ[i] != NULL; i++){ if((strncmp(name,environ[i],len) == 0) && (environ[i][len] == '=')){ olen = strlen(&environ[i][len+1]; if(olen >= buflen){ pthread_mutex_unlock(&env_mutex); return ENOSPC; } strcpy(buf,&environ[i][len+1]; pthread_mutex_unlock(&env_mutex); return 0; } } pthread_mutex_unlock(&env_mutex); return ENOENT; }
5、线程私有数据
线程私有数据是用来存储和查找与线程相关数据的一种机制。之所以叫私有数据,是因为每一个线程都访问它自己的那份数据的拷贝,不需要考虑
和其他线程同步访问数据。
使用它的原因:
1、有时候我们需要维护每个线程一份数据。虽然我们可以使用线程id作为可以的hash来做,但是还需要互斥访问和保护,防止其他线程访问该线程
的私有数据。
2、提供了将基于进程的结构适配成多线程环境的方法。比如errno
在申请和线程相关的数据之前,我们需要创建key,来和这个数据关联,我们以后会使用这个key来访问这个数据。
#include <pthread.h> int pthread_key_create(pthread_key_t *keyp; void (*destructor)(void *));
多个线程可以使用同一个key,但是每个线程要关联不同的该线程私有的数据地址。当线程退出时(pthread_exit,return),数据的地址被设置为NULL,
参数二指定的destructor将会被调用,并将该私有数据地址作为参数传递到该函数中。
但是如果线程调用的是exit,_exit,_Exit后者abort或者其他不正常退出方法,destructor不会被调用。线程经常使用malloc申请线程私有数据,这个
destructor用于释放该数据申请的内存。
一个线程可以用多个key来关联线程的私有数据,可以使用相同的destructor,也可以不同。
如果我们想将该key和关联的私有数据断开,那么可以通过调用pthread_key_delete来完成:
#include <pthread.h> int pthread_key_delete(pthread_key_t *key);
调用这个行数不会触发上面我们说的destructor的调用,我们需要采取其他的措施来释放。
我们需要保证key不被改变。
我们可能使用下面的错误的代码来做初始化,以保证不被多次初始化:
void destructor(void *); pthread_key_t key; int init_done = 0; int threadfunc(void *arg){ if(! init_done){ init_done = 1; err = pthread_key_create(&key,destructor); } //... }
这个由于线程条件竞争,可能被初始化多次或者看到不一致的状态。使用pthread_once可以解决这个问题:
#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只被调用一次:
void destructor(void *); pthread_key_t key; pthread_once_t init_done = PTHREAD_ONCE_INIT; void thread_init(void){ err = pthread_key_create(&key,destructor); } int threadfunc(void *arg){ pthread_once(&init_done,thread_init); //... }
一旦key被创建,我们就可以通过调用pthread_setspecific将key和私有数据关联,可以通过调用pthread_getspecific来通过key获得
关联的数据。
#include <pthread.h> void *pthread_getspecific(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *value);
我们前面通过改变getenv_r来改造了一个线程安全的获得环境变量的函数,如果我们不能够修改函数接口,那该如何去做?我们可以通过
使用线程私有数据来维护每个线程一个数据buffer的拷贝。
例子:
#include <limits.h> #include <string.h> #include <pthread.h> #include <stdlib.h> static pthread_key_t key; static pthread_once_t init_done = PTHREAD_ONCE_INIT; pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER; extern char **environ; static void thread_init(void){ pthread_key_create(&key,free); } char *getenv(const char *name){ int i, len; char *envbuf; pthread_once(&init_done, thread_init); pthread_mutex_lock(&env_mutex); envbuf = (char *)pthread_getspecific(key); if(envbuf == NULL){ envbuf = malloc(ARG_MAX); if(envbuf == null){ pthread_mutex_unlock(&env_mutex); return NULL; } pthread_seetspecific(key,envbuf); } len = strlen(name); for(i = 0; environ[i] != NULL; i++){ if((strncmp(name,environ[i],len) == 0) && (environ[i][len] == '=')){ strcpy(envbuf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return envbuf); } } pthread_mutex_unlock(&env_mutex); return NULL; }
6、取消选项
两个属性没有包含在pthread_attr_t结构中:可取消状态和取消类型。取消状态可以取:PTHRAD_CANCEL_ENABLE和PTHREAD_CANCEL_DISABLE。
可以通过调用pthread_setcancelstate:
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate);
在一个原子操作中,将当前可取消状态设置成state,并将以前的状态设置到oldstate。
默认pthread_cancel并不使线程立即停止,线程会继续运行到取消点,一个取消点是线程检查是否被取消的地方。一个线程在调用一下函数时,
会检查是否有退出请求:
引用
accept、mq_timedsend、putpmsg、sigsuspend、aio_suspend、msgrcv、pwrite、sigtimedwait、clock_nanosleep、msgsnd、read
sigwait、close、msync、readv、sigwaitinfo、connect、nanosleep、recv、sleep 、creat、open、recvfrom、system 、fcntl2
pause、recvmsg、tcdrain 、fsync、poll、select、usleep、getmsg、pread、sem_timedwait、wait、getpmsg、pthread_cond_timedwait
sem_wait、waitid 、lockf、pthread_cond_wait、send、waitpid、mq_receive、pthread_join、sendmsg、write 、mq_send
pthread_testcancel、sendto、writev 、mq_timedreceive、putmsg、sigpause
如果应用程序很长时间没有没有调用上面所列的函数,那么可以通过pthread_testcancel在程序中添加自己的取消点。
#include <pthread.h> void pthread_testcancel(void);
我们可以通过pthread_setcanceltype来改变取消的类型,而不是保持上面说的默认的行为:
#include <pthread.h> int pthread_setcanceltype(int type, int *oldtype);
type可以被设置为:PTHREAD_CANCEL_DEFERRED或者PTHREAD_CANCEL_ASYNCHRONOUS.返回以前的type,设置到oldtype中。
异步取消PTHREAD_CANCEL_ASYNCHRONOUS不同于PTHREAD_CANCEL_DEFERRED,可以在任何时候被取消,没有必要到达一个取消点才被取消。
发表评论
-
【zz】Vim的分屏
2012-04-26 11:13 1595分屏启动Vim 使用大写 ... -
bash的几种for循环
2012-04-26 11:13 18451、罗列式 for VARIABLE in 1 2 3 ... -
【zz】几个简单选项让你的Readline(Bash)更好用
2011-10-10 23:08 2419Reddit上的一位仁兄贴出了他的readline手册学习成果 ... -
pipe in -exec
2011-09-27 22:35 1424一个简单的例子: 将一堆的.txt文件,合并成一个文件,让每个 ... -
[zz]Unix调试的瑞士军刀:lsof
2011-08-31 10:33 1498原文见:http://heikezhi.com/2011/06 ... -
[zz]关于xargs,你可能不知道的
2011-08-29 23:05 2473如果你曾经花了许多时间在Unix命令行上,那么或许你已经见过x ... -
使用scala.sys.process包和系统交互
2011-07-19 00:00 6488在Java中我们可以使用Runtime.getRuntime( ... -
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法
2011-06-23 22:17 14462在笔记本安装Ubuntu11.04增强功能失败 引用 fuli ... -
awk getline
2011-06-02 23:58 5832awk getline开始一直用做按行读取文件。 getlin ... -
[zz]服务器性能评估
2011-04-29 14:17 4022工作这么久了,主 ... -
[zz]Top命令VIRT,RES,SHR,DATA的含义
2011-04-10 15:50 4123VIRT:virtual memory usage ... -
[zz]一些shell命令
2011-04-10 15:35 15051.显示消耗内存/CPU最多的10个进程 ps aux | ... -
Open VPN免密码配置
2011-03-03 22:55 3589公司VPN的帐号密码使用的是个超长的随机数,每次输入很麻烦,如 ... -
Linux下使用RTX腾讯通
2011-03-03 22:46 10785在公司为了使用RTX,专门安装了一个XP的虚拟机,但是这个也不 ... -
[zz]linux 常用命令总结:
2010-12-11 21:30 1688linux 常用命令总结: 一。 通用命令: 1. dat ... -
Ubuntu10.10解决Empathy无法链接MSN的问题
2010-10-21 16:36 37471.在Empathy中删除无法登录的MSN账户,并关闭Empa ... -
[zz]Vim的分屏功能
2010-10-21 13:09 1691分屏启动Vim 使用大写 ... -
Ubuntu10.10 64bit使用Eclipse插件subclipse问题
2010-10-20 20:32 1959升级到ubuntu10.10什么坑都碰到了,郁闷啊。发现sub ... -
scim输入法,Eclipse代码提示之后失去焦点的问题
2010-10-20 13:36 3086突然发现Eclipse,每次代码提示之后无法打字,感觉是文本域 ... -
Linux下使用谷歌输入法
2010-10-18 23:01 3133Linux的中文输入法一直太烂,scim终于出来对google ...
相关推荐
Linux系统编程笔记涉及到的内容广泛,涵盖了从基础的出错处理到进程管理,从内存管理到进程间通信,以及守护进程设计等多个层面的知识。下面详细说明各个部分的知识点: 1. 常见出错处理 - abort函数用于异常终止...
### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭由 `FILE *` 指向的文件...
Linux系统是一个免费使用和自由传播的类Unix操作系统,基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统,Linux是许多企业...
【Linux 系统编程与内核驱动开发笔记】 在深入探讨Linux系统编程和内核驱动开发之前,我们首先要理解Linux操作系统的基本概念。Linux是一种自由、开放源码的类Unix操作系统,广泛应用于服务器、桌面环境以及各种...
Linux系统是一个免费使用和自由传播的类Unix操作系统,基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统,Linux是许多企业...
根据提供的文件信息,我们可以推断出这是一份关于Linux编程学习笔记的PDF资料。下面将对这份资料可能涉及的关键知识点进行详细的阐述。 ### Linux编程基础知识 #### 1. Linux操作系统概述 - **定义与特点**:Linux...
总的来说,"Linux系统编程及网络编程笔记"会详细解析上述各个方面,包括理论知识、实际示例和最佳实践,旨在帮助学习者深入理解Linux环境下的系统编程和网络编程,为开发高效、安全的系统级应用程序和网络服务打下...
在Linux系统中,多线程编程是一种常见的提升程序并发性能的技术。它允许一个进程内同时执行多个线程,每个线程都有自己的执行上下文,但共享同一份内存空间。这使得线程间通信更为便捷,同时也增加了编程的复杂性。 ...
Linux系统是一个免费使用和自由传播的类Unix操作系统,基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统,Linux是许多企业...
线程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板
线程同步-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板
Linux系统是一个免费使用和自由传播的类Unix操作系统,基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统,Linux是许多企业...
多线程学习笔记 iOS开发中,多线程是一种常见的技术手段,用于优化应用程序的性能,提升用户体验。多线程的核心是让程序能够并发地执行多个任务,合理地利用设备的计算能力,尤其是在拥有多个核心的处理器上。 ...
学习笔记分为多个阶段,从基础的 Linux 平台开发到高级的 Unix 环境编程,并涉及到 Linux 应用系统开发和嵌入式开发等方面。 Linux 基础知识 Linux 是一个开源、免费的操作系统,其稳定性、安全性、处理多并发已经...
在深入探讨Linux下C语言编程中进程和线程的创建之前,我们先来理解一下进程与线程的基本概念。 - **进程**:是操作系统进行资源分配和调度的基本单位,每个进程都有独立的地址空间、内存和系统资源。在Linux环境下...
Posix多线程编程学习笔记1~6,包括线程基础,线程属性,信号灯,条件变量,互斥变量,共享内存六部分内容。很有助于linux下多线程的开发。文档主要包含介绍相应的pthread接口函数,并举了部分相应的例子。
综上所述,Linux进程线程编程的学习笔记整理涵盖了进程的基本概念、进程的创建与管理、进程类型、进程状态转换、线程编程、进程间通信和信号处理等多个方面。掌握这些知识点对于深入理解Linux操作系统运行机制、编写...