一 线程基础
1)线程全称控制线程
2)多线程的优势:
a) 比进程方便,可以共享相同的内存空间及文件描述符
b) 可以用于多个任务,而这些任务如果用单进程来实现是串行,
在多线程里面由于CPU的调度可以实现穿插执行
c) 用于交互程序,将用户输入输出与其他部分分开,优化性能
3)如何知道系统是否多线程pthread(POSIX线程)
a) #ifdef _POSIX_THREADS
b) sysconf(_SC_THREADS)
4)一个线程的数据结构
线程ID,一组寄存器,栈,调度优先级,信号屏蔽字,errno,私有数据
pthread_t 被实现为结构体这种才是可移植的
但linux 直接这样定义了 typedef unsigned long int pthread_t;
5)线程id比较 pthread_equal(pthread_t ptd1, pthread_t ptd2)
获得线程ID pthread_self(void)
6)线程创建 pthread_create(新线程的id,线程的属性,线程执行地址,函数参数)
****新线程和主线程在创建后是竞争运行
****linux上是靠clone创建子进程来实现的,所以同一个进程里面的线程获取到pid可能不同(fedora8是相同的)
7)线程终止
如果任一线程调用了exit,_Exit,_exit,那么整个进程就会终止
a)从启动函数返回
b)被其他线程取消
c)线程调用pthread_exit
8)线程阻塞
pthread_join(pthread_t aim_ptd) 阻塞当前线程一直到目标线程退出 类似于进程控制里面的wait
不能对分离状态(设置为,或已经是detach状态)的线程使用pthread_join,会返回失败
只有一个线程可以对某个线程使用pthread_join
9)请求取消某个线程
pthread_cancel
10)登记线程退出清理函数,类似于atexit登记函数
pthread_cleanup_push(函数指针,参数)
除了pthread_exit,及响应取消请求而退出的时候执行清理函数,也可以调用pthread_cleanup_pop来执行函数
线程return退出是不会执行清理函数的
pthread_cleanup_pop(int execute) execute为0就不执行清理函数,只是删除该处理函数,必须大于0才调用
11)分离线程 pthread_detach
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, THREAD_FUNCTION, arg);
可以设置线程为默认的detach,好让操作系统在线程结束的时候,回收默认的资源
二 线程同步
原因: 现代计算机体系结构造成了数据不是顺序一致来实现, 还有程序里面的逻辑,不是原子操作而造成的
1)互斥量 本质上就是多放一块全局条件变量
pthread_mutex_t 访问前加锁,访问完解锁
使用步骤:
a)创建并初始化
静态的pthread_mutex_t 赋值常量 PTHREAD_MUTEX_INITIALIZER
动态分配的pthread_mutext_t ,需要调用pthread_mutex_init初始化, pthread_mutex_destroy释放
通过pthread_mutex_init可以指定互斥量的属性,默认属性输入参数null
b)加锁
pthread_mutex_lock,如果已经有锁则阻塞到互斥变量解锁
尝试加锁 pthread_mutex_trylock 加锁不成功会马上返回 EBUSY
c)访问数据
d)解锁
pthread_mutex_unlock 解锁
2)避免互斥量死锁
产生的原因: 线程对同一个互斥量加锁2次,线程本身会陷入死锁, 多个互斥量被多个线程以相反的顺序访问的时候
3)读写锁 又名 共享-独占锁
类型: 读锁 写锁
数据类型: pthread_rwlock_t
存在条件: 写锁一次最多一个线程占据,读锁可以并行存在多个
加锁条件:
a)当前锁是写锁,所有其它新加锁都会被阻塞
b)当前锁是读锁,如果新加锁是读锁,那么可以获得访问权限
c)当前锁是读锁,如果新加锁是写锁,那么该锁会被阻塞直到所有读锁解锁。
****注意 为避免长期的读锁占据写的时间,但有写锁进来后,后续的读锁都会被拒绝掉
****适合读的次数大于写的次数的情况
初始化及释放:
pthread_rwlock_init
pthread_rwlock_destroy
加锁及解锁
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_unlock
尝试加锁
pthread_rwlock_tryrdlock
pthread_rwlock_trywrlock
4)条件变量
数据类型 pthread_cond_t
需要和互斥变量一起使用,受到互斥变量的保护
静态初始化 赋值常量 PTHREAD_COND_INITIALIZER
动态初始化和释放
pthread_cond_init pthread_cond_destroy
作用:
给多个线程提供了一个会合的场所,允许线程以无竞争的方式等待特定的条件发生
pthread_cond_wait
目的: 是通过互斥量的保护,讲线程加入到某个等待条件的线程队列里面去
过程: 进入函数前,对互斥量加锁
函数内部,将线程加入到队列并解锁
函数返回,再次对互斥量加锁
pthread_cond_timedwait
同上,但是有个时间等待限制,时间是绝对值,即具体的时刻,数据类型 timespec
唤醒等待条件的线程:
pthread_cond_signal 唤醒等待某个条件的一个线程
pthread_cond_broadcast 唤醒等待某个条件的所有线程
学习条件变量用法的2个例子
http://www.cnblogs.com/yuallen/archive/2010/05/18/1738139.html
http://www.cnblogs.com/hnrainll/archive/2011/05/01/2033903.html
三 线程控制
1)线程属性
a)数据类型 pthread_attr_t
b)初始化及释放属性结构 pthread_attr_init pthread_attr_destroy
c)获取或设置线程分离状态 pthread_attr_setdetachstate pthread_attr_getdetachstate
有2种可选的状态值:
PTHREAD_CREATE_DETACHED 分离状态
PTHREAD_CREATE_JOINABLE 正常状态,可以使用pthread_join来获取状态
d)应该获取pthread_atr_destroy的返回值,因为使用pthread_attr_init初始化的时候可能分配有内存,如果释放内存失败的话,
会造成内存泄漏
e)控制线程栈的空间的大小
需求:多个线程的栈空间累计超过了进程的可用虚拟地址空间
线程调用函数的自动变量很多,或者递归很深
1)管理stackaddr线程属性,管理stacksize线程属性
pthread_attr_getstack
pthread_attr_setstack
2)获取或设置线程栈的大小
pthread_attr_setstacksize 系统帮助分配内存,自己不用管
pthread_attr_getstacksize
3)线程栈的保护
默认大小为宏PAGESIZE,但修改了栈属性后,这个值就会变成0
pthread_attr_getguardsize
pthread_attr_setguardsize
f)线程属性-并发度
pthread_attr_setconcurency
pthread_attr_getconcurency
2)实现同步的3种方式中的对象的属性
a)互斥量属性 pthread_mutexattr_t
1)初始化及释放 pthread_mutexattr_init pthread_mutexattr_destroy
2)进程共享属性
获取与设置共享属性 pthread_mutexattr_getpshared pthread_mutexattr_setpshared
PTHREAD_PROCESS_PRIVAE 进程内的多个线程可以访问同一个同步对象
PTHREAD_PROCESS_SHARED 多个进程可以共享同一块内存区域 内存共享技术
b)互斥量类型属性
PTHREAD_MUTEX_NORMAL 标准的互斥量类型,不做错误检查或死锁检查
PTHREAD_MUTEX_DEFAULT 依赖于操作系统提供到其他类型的映射
PTHREAD_MUTEX_ERRORCHECK 提供错误检查
PTHREAD_MUTEX_RECURSIVE 允许多次加锁,但是需要解锁对应次数 tmd,这个类型叫做递归锁
获取与设置互斥量类型属性
pthread_mutexattr_gettype pthread_mutexattr_settype
c)读写锁属性 pthread_rwlockattr_t
只支持进程共享属性
d)条件变量属性pthread_condattr_t
只支持进程共享属性
四 线程重入
1)线程安全:如果一个函数同一时刻可以被多个线程安全地调用
2)系统是否支持线程安全函数 sysconf(_POSIX_THREAD_SAFE_FUNCTIONS)
3)异步-信号安全:如果函数对异步信号处理程序的重入是安全的
4)锁文件的3个函数
flockfile ftrylockfile funlockfile
该锁是递归锁
5)确保函数在进程里面只被调用一次
pthread_once_t var = PTHREAD_ONCE_INIT;
pthread_once(&var, function);
五 线程私有数据
1)需要的数据类型: pthread_key_t
2)创建私有数据的步骤
pthread_key_t key;
a)pthread_key_create(&key, 清理函数地址) 一般通过pthread_once确保函数只被执行一次,变量只被初始化一次
b)char* addr = pthread_getspecific(&key)
c)为addr分配内存 malloc
d)pthread_setspecific(&key, addr);
e)pthread_key_delete删除key
f)线程退出,执行清理函数地址
六 线程取消
1)线程可以被设置为是否可取消
pthread_setcancelstate(int state, int* oldstate)
2)pthread_cancel只是一个申请,只有线程到达了取消点才会取消.
3)延迟取消pthread_testcancel, 适合于没有取消点的函数
4)设置取消的类型pthread_setcanceltype
七 线程与IO
pread,pwrite 原子io操作
八 线程与信号
每个线程有自己的信号屏蔽字,但是他们共享
1)相同的信号处理函数 2)该信号与某函数的绑定,一个信号绑定到某个函数,这个被所有线程共享, 他们只能看到一个
多个线程公用进程的信号屏蔽机制,除了2种情况以外:
硬件故障的信号与计时器超时的信号,只递送给某个线程,其它的信号会发送给所有线程
pthread_sigmask
sigwait 等待信号发送. 一般操作需要先阻塞信号,sigwait调用会取消信号的阻塞状态,直到新信号到来
pthread_kill
sigwait(sigset_t*, int* signo)
sigwait的参数2表示捕获到的信号值
九 线程与fork
pthread_atfork,理论内容相当多,过滤掉
十 同一进程的所有线程共享同一个计时器
十一 同一进程的所有线程共享相同的文件描述符
1)线程全称控制线程
2)多线程的优势:
a) 比进程方便,可以共享相同的内存空间及文件描述符
b) 可以用于多个任务,而这些任务如果用单进程来实现是串行,
在多线程里面由于CPU的调度可以实现穿插执行
c) 用于交互程序,将用户输入输出与其他部分分开,优化性能
3)如何知道系统是否多线程pthread(POSIX线程)
a) #ifdef _POSIX_THREADS
b) sysconf(_SC_THREADS)
4)一个线程的数据结构
线程ID,一组寄存器,栈,调度优先级,信号屏蔽字,errno,私有数据
pthread_t 被实现为结构体这种才是可移植的
但linux 直接这样定义了 typedef unsigned long int pthread_t;
5)线程id比较 pthread_equal(pthread_t ptd1, pthread_t ptd2)
获得线程ID pthread_self(void)
6)线程创建 pthread_create(新线程的id,线程的属性,线程执行地址,函数参数)
****新线程和主线程在创建后是竞争运行
****linux上是靠clone创建子进程来实现的,所以同一个进程里面的线程获取到pid可能不同(fedora8是相同的)
7)线程终止
如果任一线程调用了exit,_Exit,_exit,那么整个进程就会终止
a)从启动函数返回
b)被其他线程取消
c)线程调用pthread_exit
8)线程阻塞
pthread_join(pthread_t aim_ptd) 阻塞当前线程一直到目标线程退出 类似于进程控制里面的wait
不能对分离状态(设置为,或已经是detach状态)的线程使用pthread_join,会返回失败
只有一个线程可以对某个线程使用pthread_join
9)请求取消某个线程
pthread_cancel
10)登记线程退出清理函数,类似于atexit登记函数
pthread_cleanup_push(函数指针,参数)
除了pthread_exit,及响应取消请求而退出的时候执行清理函数,也可以调用pthread_cleanup_pop来执行函数
线程return退出是不会执行清理函数的
pthread_cleanup_pop(int execute) execute为0就不执行清理函数,只是删除该处理函数,必须大于0才调用
11)分离线程 pthread_detach
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, THREAD_FUNCTION, arg);
可以设置线程为默认的detach,好让操作系统在线程结束的时候,回收默认的资源
二 线程同步
原因: 现代计算机体系结构造成了数据不是顺序一致来实现, 还有程序里面的逻辑,不是原子操作而造成的
1)互斥量 本质上就是多放一块全局条件变量
pthread_mutex_t 访问前加锁,访问完解锁
使用步骤:
a)创建并初始化
静态的pthread_mutex_t 赋值常量 PTHREAD_MUTEX_INITIALIZER
动态分配的pthread_mutext_t ,需要调用pthread_mutex_init初始化, pthread_mutex_destroy释放
通过pthread_mutex_init可以指定互斥量的属性,默认属性输入参数null
b)加锁
pthread_mutex_lock,如果已经有锁则阻塞到互斥变量解锁
尝试加锁 pthread_mutex_trylock 加锁不成功会马上返回 EBUSY
c)访问数据
d)解锁
pthread_mutex_unlock 解锁
2)避免互斥量死锁
产生的原因: 线程对同一个互斥量加锁2次,线程本身会陷入死锁, 多个互斥量被多个线程以相反的顺序访问的时候
3)读写锁 又名 共享-独占锁
类型: 读锁 写锁
数据类型: pthread_rwlock_t
存在条件: 写锁一次最多一个线程占据,读锁可以并行存在多个
加锁条件:
a)当前锁是写锁,所有其它新加锁都会被阻塞
b)当前锁是读锁,如果新加锁是读锁,那么可以获得访问权限
c)当前锁是读锁,如果新加锁是写锁,那么该锁会被阻塞直到所有读锁解锁。
****注意 为避免长期的读锁占据写的时间,但有写锁进来后,后续的读锁都会被拒绝掉
****适合读的次数大于写的次数的情况
初始化及释放:
pthread_rwlock_init
pthread_rwlock_destroy
加锁及解锁
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_unlock
尝试加锁
pthread_rwlock_tryrdlock
pthread_rwlock_trywrlock
4)条件变量
数据类型 pthread_cond_t
需要和互斥变量一起使用,受到互斥变量的保护
静态初始化 赋值常量 PTHREAD_COND_INITIALIZER
动态初始化和释放
pthread_cond_init pthread_cond_destroy
作用:
给多个线程提供了一个会合的场所,允许线程以无竞争的方式等待特定的条件发生
pthread_cond_wait
目的: 是通过互斥量的保护,讲线程加入到某个等待条件的线程队列里面去
过程: 进入函数前,对互斥量加锁
函数内部,将线程加入到队列并解锁
函数返回,再次对互斥量加锁
pthread_cond_timedwait
同上,但是有个时间等待限制,时间是绝对值,即具体的时刻,数据类型 timespec
唤醒等待条件的线程:
pthread_cond_signal 唤醒等待某个条件的一个线程
pthread_cond_broadcast 唤醒等待某个条件的所有线程
学习条件变量用法的2个例子
http://www.cnblogs.com/yuallen/archive/2010/05/18/1738139.html
http://www.cnblogs.com/hnrainll/archive/2011/05/01/2033903.html
三 线程控制
1)线程属性
a)数据类型 pthread_attr_t
b)初始化及释放属性结构 pthread_attr_init pthread_attr_destroy
c)获取或设置线程分离状态 pthread_attr_setdetachstate pthread_attr_getdetachstate
有2种可选的状态值:
PTHREAD_CREATE_DETACHED 分离状态
PTHREAD_CREATE_JOINABLE 正常状态,可以使用pthread_join来获取状态
d)应该获取pthread_atr_destroy的返回值,因为使用pthread_attr_init初始化的时候可能分配有内存,如果释放内存失败的话,
会造成内存泄漏
e)控制线程栈的空间的大小
需求:多个线程的栈空间累计超过了进程的可用虚拟地址空间
线程调用函数的自动变量很多,或者递归很深
1)管理stackaddr线程属性,管理stacksize线程属性
pthread_attr_getstack
pthread_attr_setstack
2)获取或设置线程栈的大小
pthread_attr_setstacksize 系统帮助分配内存,自己不用管
pthread_attr_getstacksize
3)线程栈的保护
默认大小为宏PAGESIZE,但修改了栈属性后,这个值就会变成0
pthread_attr_getguardsize
pthread_attr_setguardsize
f)线程属性-并发度
pthread_attr_setconcurency
pthread_attr_getconcurency
2)实现同步的3种方式中的对象的属性
a)互斥量属性 pthread_mutexattr_t
1)初始化及释放 pthread_mutexattr_init pthread_mutexattr_destroy
2)进程共享属性
获取与设置共享属性 pthread_mutexattr_getpshared pthread_mutexattr_setpshared
PTHREAD_PROCESS_PRIVAE 进程内的多个线程可以访问同一个同步对象
PTHREAD_PROCESS_SHARED 多个进程可以共享同一块内存区域 内存共享技术
b)互斥量类型属性
PTHREAD_MUTEX_NORMAL 标准的互斥量类型,不做错误检查或死锁检查
PTHREAD_MUTEX_DEFAULT 依赖于操作系统提供到其他类型的映射
PTHREAD_MUTEX_ERRORCHECK 提供错误检查
PTHREAD_MUTEX_RECURSIVE 允许多次加锁,但是需要解锁对应次数 tmd,这个类型叫做递归锁
获取与设置互斥量类型属性
pthread_mutexattr_gettype pthread_mutexattr_settype
c)读写锁属性 pthread_rwlockattr_t
只支持进程共享属性
d)条件变量属性pthread_condattr_t
只支持进程共享属性
四 线程重入
1)线程安全:如果一个函数同一时刻可以被多个线程安全地调用
2)系统是否支持线程安全函数 sysconf(_POSIX_THREAD_SAFE_FUNCTIONS)
3)异步-信号安全:如果函数对异步信号处理程序的重入是安全的
4)锁文件的3个函数
flockfile ftrylockfile funlockfile
该锁是递归锁
5)确保函数在进程里面只被调用一次
pthread_once_t var = PTHREAD_ONCE_INIT;
pthread_once(&var, function);
五 线程私有数据
1)需要的数据类型: pthread_key_t
2)创建私有数据的步骤
pthread_key_t key;
a)pthread_key_create(&key, 清理函数地址) 一般通过pthread_once确保函数只被执行一次,变量只被初始化一次
b)char* addr = pthread_getspecific(&key)
c)为addr分配内存 malloc
d)pthread_setspecific(&key, addr);
e)pthread_key_delete删除key
f)线程退出,执行清理函数地址
六 线程取消
1)线程可以被设置为是否可取消
pthread_setcancelstate(int state, int* oldstate)
2)pthread_cancel只是一个申请,只有线程到达了取消点才会取消.
3)延迟取消pthread_testcancel, 适合于没有取消点的函数
4)设置取消的类型pthread_setcanceltype
七 线程与IO
pread,pwrite 原子io操作
八 线程与信号
每个线程有自己的信号屏蔽字,但是他们共享
1)相同的信号处理函数 2)该信号与某函数的绑定,一个信号绑定到某个函数,这个被所有线程共享, 他们只能看到一个
多个线程公用进程的信号屏蔽机制,除了2种情况以外:
硬件故障的信号与计时器超时的信号,只递送给某个线程,其它的信号会发送给所有线程
pthread_sigmask
sigwait 等待信号发送. 一般操作需要先阻塞信号,sigwait调用会取消信号的阻塞状态,直到新信号到来
pthread_kill
sigwait(sigset_t*, int* signo)
sigwait的参数2表示捕获到的信号值
九 线程与fork
pthread_atfork,理论内容相当多,过滤掉
十 同一进程的所有线程共享同一个计时器
十一 同一进程的所有线程共享相同的文件描述符