struct task_struct {
...
pid_t pid;//线程id
pid_t tgid;//进程id
...
struct task_struct *group_leader;//主线程
...
struct list_head thread_group;//线程组
...
};
获取线程ID:pid_t gettid(void)
获取进程ID:pid_t getpid(void)
/*
thread:返回线程ID//这里用的是pthread_self获得tid号
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
成功返回0,失败返回错误码
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
/*
*获取POSIX描述的线程ID
*用来区分某个进程中不同的线程,当一个线程退出后,新创建的线程可以复用原来的id
*/
pthread_t pthread_self(void);
/*
*gettid获取的是内核中线程ID
*对于单线程的进程,内核中tid=pid;对于多线程进程,pid相同tid不同;tid用于描述内核真实的pid和tid信息
*/
pid_t gettid(void);
/*
*pthread_exit返回的指针所指向的内存单元必须是全局的或者malloc分配的
*/
void pthread_exit(void *value_ptr);
/*
*thread:线程ID
*成功返回0,失败返回错误码
*系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程
*若线程函数没有系统调用,则在线程函数中调用pthread_testcancel(),让内核去检测是否需要取消当前线程
*/
int pthread_cancel(pthread_t thread);
/*
*thread:线程ID
*value_ptr:指向一个指针,指向线程的返回值
*成功返回0,失败返回错误码
*/
//如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数
int pthread_join(pthread_t thread, void **value_ptr);
pthread_detach(pthread_t thread);
#include
pthread_mutex_t mutex_x = PTHREAD_MUTEX_INITIALIZER;
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_mutex_destroy(pthread_mutex_t *mutex);
初始化:
//静态
pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;
//动态
/*
*参数attr指定了新建互斥锁的属性。若参数attr为空(NULL),则使用默认的互斥锁属性,默认属性为快速互斥锁
*成功返回0,失败返回错误码
*/
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
互斥锁相关属性
//初始化互斥锁属性
pthread_mutexattr_init(pthread_mutexattr_t attr);
//销毁互斥锁属性
pthread_mutexattr_destroy(pthread_mutexattr_t attr);
//用于获取互斥锁属性
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr , int *restrict pshared);
//用于设置互斥锁属性
/*属性值:
*PTHREAD_PROCESS_PRIVATE:锁只能用于一个进程内部的两个线程进行互斥(默认情况)
*PTHREAD_PROCESS_SHARED:锁可用于两个不同进程中的线程进行互斥,使用时还需要在进程共享内存中分配互斥锁,然后为该互斥锁指定属性
*/
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr , int pshared);
//获取互斥锁类型
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr , int *restrict type);
//设置互斥锁类型
/*类型:
*PTHREAD_MUTEX_NOMAL:标准互斥锁,第一次上锁成功,第二次上锁会失败并阻塞
*PTHREAD_MUTEX_RECURSIVE:递归互斥锁,第一次上锁成功,第二次上锁还是会成功,可以理解为内部有一个计数器,每加一次锁计数器加1,解锁减1
*PTHREAD_MUTEX_ERRORCHECK:检查互斥锁,第一次上锁会成功,第二次上锁出错返回错误信息,不会阻塞
*PTHREAD_MUTEX_DEFAULT:默认互斥锁,第一次上锁会成功,第二次上锁会失败
*/
int pthread_mutexattr_settype(const pthread_mutexattr_t *restrict attr , int type);
#include
pthread_cond_t qready = PTHREAD_COND_INITIALIZER; //cond
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; //mutex
/*
*函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞cv参数指向的条件变量上
*被阻塞的线程可以被pthread_cond_signal、pthread_cond_broadcast函数唤醒,也可以被信号中断后唤醒
*pthread_cond_wait函数的返回并不意味条件满足,必须重新检查条件的值
*pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使函数出错返回
*成功返回0,失败返回失败码
*/
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);
/*
*函数到一定时间,即使条件未发生也会解除阻塞
*pthread_cond_timedwait函数也是退出点
*成功返回0,失败返回失败码
*/
int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mp, const structtimespec * abstime);
/*
*用于释放被阻塞在指定条件变量上的一个线程
*必须在互斥锁的保护下使用相应的条件变量,否则很容易造成死锁
*成功返回0,失败返回出错码
*/
int pthread_cond_signal(pthread_cond_t *cv);
/*
*函数唤醒所有被pthread_cond_wait函数阻塞在某个条件变量上的线程
*参数cv被用来指定这个条件变量,当没有线程阻塞在这个条件变量上时,pthread_cond_broadcast函数无效
*成功返回0,失败返回出错码
*/
int pthread_cond_broadcast(pthread_cond_t *cv);
/*
成功返回0,失败返回错误码
*/
int pthread_cond_destroy(pthread_cond_t *cv);
初始化:
//静态
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
//动态
/*
*参数cattr指定了新建条件变量的属性。若参数attr为空(NULL),则默认缺省的条件变量
*成功返回0,失败返回错误码
*/
int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *cattr);
唤醒丢失问题
唤醒丢失往往会在下面的情况下发生:
可以多个线程同时读,但是不能多个线程同时写
#include
//静态初始化
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
//动态初始化,NULL代表读写锁采用默认属性
int pthread_rwlock_init(rwlock,NULL);
//销毁读写锁
/*
*在释放某个读写锁的资源之前,需要先通过pthread_rwlock_destory函数对读写锁进行清理。释放由pthread_rwlock_init函数分配的资源
*/
int pthread_rwlock_destory(rwlock);
/*
如果你想要读写锁使用非默认属性,则attr不能为NULL,得给attr赋值
*/
//给attr初始化
int pthread_rwlockattr_init(attr);
//销毁attr
int pthread_rwlockattr_destory(attr);
/*
*以写的方式获取锁,以读的方式获取锁,释放读写锁
*
*/
//以读的方式获取锁
int pthread_rwlock_rdlock(rwlock);
//以写的方式获取锁
int pthread_rwlock_wrlock(rwlock);
//释放锁
int pthread_rwlock_unlock(rwlock);
//上面两个获取锁的方式都是阻塞的函数,也就是说获取不到锁的话
//调用线程不是立即返回,而是阻塞执行
//在需要进行写操作的时候,这种阻塞式获取锁的方式是非常不好的
/*非阻塞的方式获取锁*/
int pthread_rwlock_tryrdlock(rwlock);
int pthread_rwlock_trywrlock(rwlock);
struct sem {
int semval; /* 信号量的当前值 */
int sempid; /* 上一次操作本信号的进程PID */
};
信号量(sem)和互斥锁的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区
#include
sem_t sem;
//信号量初始化
/*
*pshared为0表示这个信号量是当前进程的局部信号量
*pshared为1表示这个信号量可以在多个进程之间共享
*v为信号量的初始值
*成功返回0,失败返回-1
*/
int sem_init(&sem,pshared,v);
//信号量值的加减
//以原子操作的方式将信号量的值减去1
int sem_wait(&sem);
//以原子操作的方式将信号量的值加上1
int sem_post(&sem);
//对信号量进行清理
int sem_destory(&sem)
具体用法:
//创建和注销
/*
*不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的
*但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量
*/
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
/*
*在LinuxThreads的实现中,TSD池用一个结构数组表示
*创建一个线程私有数据就相当于将结构数组中的某一项设置为"in_use",并将其索引返回给*key,然后设置destructor函数为destr_function
*/
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = {{0, NULL}};
/*
*函数并不检查当前是否有线程正使用该线程私有数据,也不会调用清理函数(destr_function)
*而只是将TSD释放以供下一次调用pthread_key_create()使用。
*在LinuxThreads中,它还会将与之相关的线程数据项设为NULL
*/
int pthread_key_delete(pthread_key_t key);
//访问私有变量
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
//pthread.h
struct sched_param {
int sched_priority;
};
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
}pthread_attr_t;
//应先初始化线程属性,再pthread_create创建线程
/*
*初始化线程属性
*成功返回0,失败返回错误码
*/
int pthread_attr_init(pthread_attr_t *attr);
/*
*销毁线程属性所占用的资源
*成功返回0,失败返回错误码
*/
int pthread_attr_destroy(pthread_attr_t *attr);
/*
*获得/设置线程的继承性
*attr:线程属性变量
*inheritsched:线程的继承性
*若成功返回0,若失败返回-1
*继承性的可能值是PTHREAD_INHERIT_SCHED,表示新现成将继承创建线程的调度策略和参数
*PTHREAD_EXPLICIT_SCHED(缺省),表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数
*/
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);
/*
*获得/设置线程的调度策略
*attr:线程属性变量
*policy:调度策略
*若成功返回0,若失败返回-1
*调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)
*/
int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
/*
*大的优先权值对应高的优先权
*系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到
*policy:系统支持的线程优先权的最大和最小值
*若成功返回0,若失败返回-1
*/
struct sched_param
{
int sched_priority;
};
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);
/*
*获得/设置线程的调度参数
*attr:线程属性变量
*param:ched_param结构
*/
int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
如果不是编写实时程序,不建议修改线程的优先级。因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。
/*
*获得/设置线程的作用域
*attr:线程属性变量
*scope:线程的作用域
*若成功返回0,若失败返回-1
*作用域控制线程是否在进程内或在系统级上竞争资源
*值为PTHREAD_SCOPE_PROCESS(缺省),此线程将与进程中的其他线程进行竞争
*值为PTHREAD_SCOPE_SYSTEM,此线程将 与系统中的所有线程进行竞争
*/
int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(const pthread_attr_t *attr,int *scope);
/*
*设置线程属性,分离or非分离
*成功返回0,失败返回错误码
*PTHREAD_CREATE_DETACHED(分离线程)
*PTHREAD _CREATE_JOINABLE(非分离线程)
*/
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
/*
*获取程属性,分离or非分离
*成功返回0,失败返回错误码
*/
int pthread_attr_getdetachstate(pthread_attr_t *attr, int detachstate);
/*
*获得/修改线程栈的位置
*attr:指向一个线程属性的指针
*stackaddr:栈地址
*成功返回0,若失败返回-1
*/
int pthread_attr_getstackaddr(const pthread_attr_t *attr,void **stackaddf);
int pthread_attr_setstackaddr(pthread_attr_t *attr,void *stackaddr);
/*
*修改或获取栈地址的函数
*attr:指向一个线程属性的指针(由pthread_attr_init函数初始化)
*stackaddr:返回获取的栈地址
*stacksize:返回获取的栈大小
*成功返回0,若失败返回-1
*/
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);
//设置stacksize属性时,选择的stacksize不能小于PTHREAD_STACK_MIN
/*
*获得/修改线程栈的大小
*attr:指向一个线程属性的指针
*stacksize:返回线程的堆栈大小
*成功返回0,若失败返回-1
*/
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
/*
*线程属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存大小
*可以把guardsize线程属性设置为0,不允许属性的这一特征行为发生:在这种情况下,不会提供警戒缓冲区
*如果修改了栈地址stackaddr,系统就会默认由我们自己管理栈,进而使警戒缓冲区机制无效,等同于把guardsize置为0
*/
/*
*获得/修改线程栈末尾的警戒缓冲区大小
*attr:线程属性变量
*guardsize:缓冲区大小
*成功返回0,若失败返回-1
*如果guardsize被修改,操作系统可能会把它取为页大小的整数倍。如果线程的栈指针溢出到警戒区,应用程序就可能通过信号接收到出错信息
*成功返回0,失败返回错误码
*/
int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);
作用域属性描述特定线程将与哪些线程竞争资源。线程可以在两种竞争域内竞争资源:
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process):轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程
/*
*第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)
*/
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
POSIX标准指定了三种调度策略:先入先出策略 (SCHED_FIFO)、循环策略 (SCHED_RR) 和自定义策略 (SCHED_OTHER):
新线程默认使用 SCHED_OTHER 调度策略。线程一旦开始运行,直到被抢占或者直到线程阻塞或停止为止。
应用程序使用 pthread_setconcurrency() 通知系统其所需的并发级别