线程 进程
标识符类型 pthread_t pid_t
获取id pthread_self() getpid()
创建 pthread_create() fork()
int main(int argc, char *argv[])
./xxx 15 14 16
argc = 4
argv[0] = ./xxx
aggv[1] = 15
…
main函数返回,会导致主线程里的进程结束,进程内的线程也结束,可使用pthread_exit函数等待。
int pthread_join(pthread_t tid, void **rval)
阻塞该线程,直到tid线程终止,会使tid分离,如果tid已经分离会调用失败
int pthread_detach(pthread_t thread)
pthread_detach函数 可以分离一个线程,线程可以自己分离自己
int pthread_cancel(pthread_t tid)
取消tid线程,仅仅发送取消请求
int pthread_setcancelstate(int state, int *oldstate)
设置线程对取消信号的反应,忽略或者响应
int pthread_setcanceltype(int type, int *oldtype)
设置线程取消动作的执行时机。
int pthread_kill(pthread_t thread, int sig)
向线程发送signal,发送0用来判断线程是不是还活着
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
给信号signum设置一个处理函数
act.sa_mask 信号屏蔽字
act.sa_handler 信号集处理程序
int sigemptyset(sigset_t *set)
清空信号集
int sigfillset(sigset_t *set)
将所有信号加入信号集
int sigaddset(sigset_t *set, int signum)
增加一个信号到信号集
int sigdelset(sigset_t *set, int signum)
删除一个信号到信号集
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
多线程信号屏蔽处理
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。
新线程的当前信号掩码会继承创造它的线程的信号掩码
pthread_cleanup_push(void (rtn)(void), void *args)
注册处理程序
pthread_cleanup_pop(int excute)
清除处理程序
互斥量用pthread_mutex_t类型的数据表示
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
动态分配的互斥量初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
静态分配的互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
动态分配的互斥量在释放内存之前销毁
int pthread_mutex_lock(pthread_mutex_t *mutex);
加锁,会导致阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);
加锁,不会导致阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解锁,如果一个互斥量没有被锁住,那么解锁就会出错
可以先释放已经占有的锁,然后再加锁其他互斥量,防止死锁。
读写锁有三种状态,读模式下加锁,写模式下加锁,不加锁。一次只有一个线程可以占有写模式下的读写锁,但是多个线程可以同时占有读模式的读写锁。
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
读写锁的初始化
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
销毁
读模式加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
写模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
条件变量初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
销毁
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量使用需要配合互斥量
条件等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
计时等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
当条件满足的时候,唤醒等待条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_once_t once_control = PTHREAD_ONCE_INIT;
void init_routine()
{
//初始化互斥量
//初始化读写锁
…
}
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void));
保证init_routine()函数在本进程执行序列中仅执行一次。
线程属性初始化
int pthread_attr_init(pthread_attr_t *attr);
线程属性销毁
int pthread_attr_destroy(pthread_attr_t *attr);
如果在调用pthread_attr_init初始化属性的时候分配了内存空间,那么pthread_attr_destroy将释放内存空间。
可修改pthread_attr_t结构体的detachstate属性,让线程以分离状态启动。
PTHREAD_CREATE_DETACHED分离的
PTHREAD_CREATE_JOINABLE 非分离的,可连接的
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
如果用完了虚拟地址空间,可以使用malloc或者mmap来为其他栈分配空间,并修改栈的位置。
修改栈属性
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
获取栈属性
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
栈大小设置,不能小于PTHREAD_STACK_MIN
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
检查
1、在编译阶段使用
_POSIX_THREAD_ATTR_STACKADDR 和 _POSIX_THREAD_ATTR_STACKSIZE符号来检查系统是否支持线程栈属性,这些宏定义在/usr/include/bits/posix_opt.h
2、在运行阶段把
_SC_THREAD_ATTR_STACKADD和 _SC_THREAD_THREAD_ATTR_STACKSIZE传递给sysconf函数检查系统对线程栈属性的支持
线程属性guardsize控制着线程栈末尾以后用以避免栈溢出的扩展内存的大小,这个属性默认是PAGESIZE个字节。
设置guardsize
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
获取guardsize
int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize);
互斥量初始化
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
互斥量销毁
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
进程共享属性有两种值:
PTHREAD_PROCESS_PRIVATE,这个是默认值,同一个进程中的多个线程访问同一个同步对象
PTHREAD_PROCESS_SHARED, 这个属性可以使互斥量在多个进程中进行同步,如果互斥量在多进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程
进程共享属性需要检测系统是否支持,可以检测宏_POSIX_THREAD_PROCESS_SHARED
设置互斥量进程共享属性
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(pthread_mutexattr_t *attr, int type);
读写锁属性初始化
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_init(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);
条件变量属性初始化
int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
设置条件变量属性
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr, int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared);
int pthread_key_create(pthread_key_t *key, void (destructor)(voi8d));
destructor是与键相关的析构函数。当线程调用pthread_exit或者使用return返回,析构函数就会被调用。
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
将私有数据与key关联
void *pthread_getspecific(pthread_key_t key);
获取私有数据的地址,如果没有数据与key关联,那么返回空
如果父进程中互斥量是锁着的,那么在子进程中互斥量也是锁着的,因为不是子进程自己锁住的,它无法解锁。如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_mutex_lock副本,这样子进程就有机会去解锁了。
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
prepare是在fork调用之前会被调用的,parent在fork返回父进程之前调用,child在fork返回子进程之前调用。
如果在prepare中加锁所有的互斥量,在parent和child中解锁所有的互斥量,那么在fork返回之后,互斥量的状态就是未加锁。
可以有多个 pthread_atfork函数,这是也就会有多个处理程序(prepare,parent,child)。多个prepare的执行顺序与注册顺序相反,而parent和child的执行顺序与注册顺序相同