线程基础
线程是进程的一个执行单元,执行一段程序片段,线程共享全局变量;线程的查看可以使用命令或者文件来进行查看;命令:ps -T -p
线程的相关函数
线程标识
pthread_t pthread_self(void);返回值是当前线程ID,可以使用%lu打印线程pid的值;设置线程名称:int prctl(int option,unsigned long arg2);PR_GET_NAME用于获得当前线程的名字,PR_SET_NAME,用于设置当前线程的名字;arg2:线程名称最大长度为15个字节,并且应该以'\0'结尾,一共16个字符;返回值:0表示成功非0表示出错;
thread01.c
#include
#include
#include
#include
int main(){
printf("PID:%d,TID:%lu\n",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_SET_NAME,"test");
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
thread02.c
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
info();
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
sleep(1);
}
线程创建
int pthread_create(pthread_t *tidp,pthread_attr_t *attr,void (start_rtn)(void),void *arg);tidp:线程ID指针;
attr:线程属性:绑定:pthread_attr_setscope(pthread_attr_t *attr, int scope);scope:PTHREAD_SCOPE_PROCESS:表示不进行绑定,这个是默认的选项;PTHREAD_SCOPE_SYSTEM表示绑定轻进程(LWP)/内核线程;
分离:pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);detachstate:PTHREAD_CREATE_DETACHED:表示分离线程;PTHREAD_CREATE_JOINABLE表示非分离线程;
线程使用的是缺省的堆栈,线程的优先级和父进程同级别;
线程的ID指针返回值是void类型的指针函数;arg:表示的是start_rtn的形式参数;返回值0表示成功,非0表示出错;
线程消亡
线程的消亡分为正常终止和线程取消两种情况;线程正常终止:对于子线程来说线程处理函数return 返回一个返回值,线程可以终止,线程终止还可以调用pthread_exit(void* retval);来进行处理,retval:表示函数的返回指针,只要pthread_join中的第二个参数retval不是NULL,这个值江北传递给retval,如果用在线程回调函数中,将返回线程数据;
对于主线程:线程合并:int pthread_join(pthread_t tid, void **retval);tid:被等待的线程标识符,retval一个用户定义的指针,它可以用来存储被等待线程的返回值,返回值:0表示成功,非0表示错误码;当进行线程合并时,可以由其他线程来终止,并且回收资源;
pthread_join.c:线程取消函数
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
主线程终止的另一种方式是线程分离int pthread_detach(ppthread_t tid);tid:表示要释放线程的标识符,返回值0表示成功,非0表示错误码;但是这样是不能被其他线程终止的,存储在它终止时,有系统自动的回收释放;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
线程取消:取消点取消,如果线程接收到取消信号,到达指定位置才能能取消,取消点取消分为手动:void pthread_testcancle(void);自动:通过引起阻塞的系统调用来取消;
线程取消:发送取消信号:int pthread_cancel(pthread_t pthread),需要注意的是:发送成功并不一定意味着thread线程就会终止,发送CANCEL指令后,使用pthread_join 函数,等待指定的线程完全退出以后,在继续执行,否则容易产生段错误;函数的返回值0表示成功,非0值表示失败;
设置当前线程的取消类型:int pthread_setcancelstate(int state,int *oldstate);state:PTHREAD_CANCEL_ENABLE:线程取消启用;PTHREAD_CANCEL_DISABLE:表示线程取消禁用;oldstate:表示线程之前的状态;
设置当前线程的取消类型:int pthread_setcanceltype(int type,int *oldtype);type:PTHREAD_CANCEL_DEFFERED:表示取消点取消;PTHREAD_CANCEL_ASYNCHRONOUS:表示立即退出;oldstate:表示之前的取消类型;
设置取消点:void pthread_testcanel(void);
pthread_cancel.c
#include
#include
long count = 0;
void* default_func(void* arg){
for(;;){
pthread_testcancel();
count++;
}
}
void* disable_func(void* arg){
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
for(;;){
pthread_testcancel();
count++;
}
}
void* force_func(void* arg){
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
for(;;){
count++;
}
}
void* disable_force_func(void* arg){
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
for(;;){
count++;
}
}
void* watch(void* arg){
for(;;){
sleep(1);
printf("count:%d\n",count);
}
}
void* cancel(void* arg){
sleep(5);
pthread_t* ptid = arg;
printf("cancel %lu\n",ptid[0]);
pthread_cancel(ptid[0]);
}
int main(int argc,char* argv[]){
typedef void* (*Func)(void*);
Func funcs[3] = {default_func,watch,cancel};
int c;
while((c=getopt(argc,argv,"dp"))!=-1){
switch(c){
case 'd':
funcs[0] = (funcs[0] == default_func?disable_func:disable_force_func);
break;
case 'p':
funcs[0] = (funcs[0] == default_func?force_func:disable_force_func);
break;
}
}
pthread_t tids[3];
int i=0;
for(;i<3;i++){
pthread_create(&tids[i],NULL,funcs[i],&tids);
printf("create thread %lu\n",tids[i]);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
}
发送信号:int pthread_kill(pthread_t tid, int sig);tid:表示线程ID,sig:信号,0表示保留信号,用于测试线程是否存在,信号为正数表示系统自定义信号或者系统自定义信号;返回值:0表示调用成功,ESRCH:表示线程不存在;EINVAL:表示信号不合法;
设置线程并发度:并发度表示线程并发的数目,int pthread_setconcurrency(int level);获取线程并发度:int pthread_getconcurrency(void);
void pthread_cleanup_push(void (routine)(void),void *arg);void pthread_cleanup_pop(int execute);execute通常为0;关于线程的信息可以man 7 threads;
线程间通信使用全局变量的方式进行;
线程同步
信号量
信号量的创建:int sem_init(sem_t *sem,int pshared,unsigned int value);sem:表示信号量对象;pshared:表示信号量的类型,0表示线程共享,<0:表示进程共享;value:用于表示初始值;返回值0表示成功,-1表示失败;
Ã销毁:int sem_destory(sem_t *sem);sem:表示信号量对象;返回值0表示成功,-1表示失败;
等待:包括三种:阻塞等待,int sem_wait(sem_t *sem);sem:标识信号量对象;返回值0表示成功,-1表示失败;非阻塞等待:int sem_trywait(sem_t *sem);sem:表示信号量对象;返回值0表示成功,-1表示失败;超时等待:int sem_timedwait(sem_t *sem,struct timespec *abstime);sem:表示信号量对象;abstime:表示等待时间;-1表示等待超时,0表示成功;
̳触发:sem:信号量对象;0:表示成功;-1:表示成功;
互斥量
互斥量和信号量的区别:
1、互斥量用于线程的互斥,信号用于线程的同步互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排他性,但是沪指无法限制资源访问者对其资源的访问顺序,也就是访问是无序的;同步:是指在互斥的基础上,通过其它机制实现访问者对资源的有序访问,在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况 都是互斥的,少数情况是指可以允许多个访问者同时访问资源;
2、互斥量值只能是0或者1,信号量值可以作为非负整数,也就是说,一个互斥量只能用于一个资源的互斥访问,它不能解决多个资源的多线程互斥问题,信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量时,也可以完成一个资源的互斥访问;
3、互斥量的加锁和解锁必须有同一个线程使用,信号量可以由一个线程释放,另一个线程得到;
4、信号量可以允许多个线程进入临界区,互斥体只能允许一个线程进入临界区;
ATM取款,toilet;互斥量分为静态分配互斥量,实现特点是简单,pthread_mutex = PTHREAD_MUTEX_INITIAVIZER;动态分配互斥量:特点是可以设置更多的选项;pthread_mutex_init(&mutex,NULL)或者pthread_mutex_destory(&mutex);
互斥量的操作:加锁:互斥锁int pthread_mutex_lock(pthread_t *mutex);尝试加锁 int pthread_mutex_trylock(pthread_t *mutex),mutex:表示的也是互斥锁;解锁:int pthread_mutex_unlock(pthread_t *mutex),mutex同样表示的是互斥锁;
mutex01.c
#include
#include
void* func(void* arg){
pthread_mutex_lock(arg);
printf("enter func\n");
sleep(1);
printf("do something\n");
sleep(1);
printf("level func\n");
pthread_mutex_unlock(arg);
}
int main(int argc,int argv[]){
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
pthread_t tids[3];
int i;
for(i=0;i<3;i++){
pthread_create(&tids[i],NULL,func,&mutex);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
pthread_mutex_destroy(&mutex);
}
mutex02.c
#include
#include
void* func(void* arg){
pthread_cleanup_push(pthread_mutex_unlock,arg);
pthread_mutex_lock(arg);
printf("enter func\n");
sleep(1);
printf("do something\n");
sleep(1);
printf("level func\n");
pthread_exit(0);
pthread_cleanup_pop(0);
}
int main(int argc,int argv[]){
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
pthread_t tids[3];
int i;
for(i=0;i<3;i++){
pthread_create(&tids[i],NULL,func,&mutex);
}
for(i=0;i<3;i++){
pthread_join(tids[i],NULL);
}
pthread_mutex_destroy(&mutex);
}
条件变量
线程挂起直到共享数据的某些条件得到满足;条件变量分为静态分配条件变量,pthread_cond_t_cond = PTHREAD_COND_INITIALIZER特点是比较简单;动态分配静态变量:特点是可以设置更多的选项,pthread_cond_init(&cond,NULL);或者使用pthread_cond_destory(&cond);
操作:
条件等待:int pthrad_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);cond:表示条件变量;mutex:表示互斥锁;
计时等待:int pthread_cond_timedwait(pthread_cond_t *cond,pthread_muex_t *mutex,const struct timespec *abstime);cond:表示条件变量;mutex:表示互斥锁;abstime:表示等待时间;返回值:ETIMEDOUT:表示超时等待结束;
激活操作:
单个激活:int pthread_cond_signal(pthread_cond_t *cond);cond:表示条件变量,返回值0表示成功,正数表示错误码;
Ϳ全部激活: int pthread_cond_broadcast(pthread_cond_t *cond);cond:表示条件变量,返回值0表示成功,正数表示错误码;
线程读写锁
读取锁是共享锁,写入锁是独占锁;
静态分配读写锁:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER,特点是实现简单;动态分配读写锁:pthread_rwlock_init(&rwlock,NULL);pthread_rwlock_destory(&rwlock);可以用于设置更多选项
关于锁的操作
读取锁:加读取锁:int pthread_rwlock_rdlock(pthread_rwlock *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);
线程和进程的区别
接口对比:
getpid | pthread_self | 用于获得控制流的ID |
---|---|---|
fork | pthread_create | 创建新的控制流 |
exit | pthread_exit | 退出已有的控制流 |
waitpid | pthread_join | 等待控制流并获得结束代码 |
1、进程是资源分配和拥有的基本单位,线程是处理器调度的基本单位;
2、进程拥有独立的地址空间,线程共性进程的地址空间;
3、线程上下文切换比进程上下文切换要快得多;
4、子进程崩溃不会影响其它子进程,但是任何一个线程崩溃,整个程序都会崩溃;