linux线程学习(5)
来源:互联网 发布:1吨铀能发多少电 知乎 编辑:程序博客网 时间:2024/05/17 17:15
线程的高级属性
1. 一次性初始化:
- 有些事需要且只能执行一次(比如互斥量初始化)。因此有了使用一次初始(pthread_once_t);
- 首先要定义一个pthread_once_t变量,这个变量要用宏PTHREAD_ONCE_INIT初始化。然后创建一个与控制变量相关的初始化函数pthread_once_t once_control = PTHREAD_ONCE_INIT;
- 接下来就可以在任何时刻调用pthread_once函数
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void));//功能:本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定。
由pthread_once()指定的函数init_routine执行且仅执行一次
- 用once_control来表示pthread_once()的执行状态:
1、如果once_control初值为0,那么 pthread_once从未执行过,init_routine()函数会执行。
2、如果once_control初值设为1,则由于所有pthread_once()都必须等待其中一个激发”已执行一次”信号, 因此所有pthread_once ()都会陷入永久的等待中,init_routine()就无法执行
3、如果once_control设为2,则表示pthread_once()函数已执行过一次,从而所有pthread_once()都会立即 返回,init_routine()就没有机会执行
当pthread_once函数成功返回,once_control就会被设置为2
实例:创建两个线程分别调用pthread_once函数,执行初始化函数分别打印once_contrl的值。
#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>pthread_once_t once_contrl=PTHREAD_ONCE_INIT;//定义pthread_once_t变量,在pthread_once函数中用于还回init_routine函数执行状态void init_routine(void){ //这个函数只会执行一次 printf("我是初始化函数,我只执行一次\n");}void * init_func1(void * arg){ printf("this is pthread %d excute,once_contrl = %d\n",(int)arg,once_contrl); pthread_once(&once_contrl,init_routine); printf("the once_contrl = %d\n",once_contrl); return NULL;}void * init_func2(void * arg){ sleep(2); printf("this is pthread %d excute,once_contrl = %d\n",(int)arg,once_contrl); pthread_once(&once_contrl,init_routine); printf("the once_contrl = %d\n",once_contrl); return NULL;}int main(){ pthread_t pid1,pid2; //创建两个线程。进行一次性初始化 pthread_create(&pid1,NULL,init_func1,(void*)1); pthread_create(&pid2,NULL,init_func2,(void*)2); //连接线程 pthread_join(pid1,NULL); pthread_join(pid2,NULL); return 0;}
运行结果:
线程1先执行打印once_contrl =0,因为这时还没调用pthread_once函数,执行初始化init_routine函数,然后调用执行,
once_control设为2,则表示pthread_once()函数已执行过一次,从而其他线程调用pthread_once()都会立即 返回,init_routine()就没有机会再执行
2. 线程属性(即我们使用pthread_create()函数中的第二个参数,与待会介绍的线程同步属性的类型不同)
- 线程的属性用pthread_attr_t类型的结构表示,在创建线程的时候可以不用传入NULL,而是传入一个pthread_attr_t结构,由用户自己来配置线程的属性
- 线程的分离属性
- 分离属性的概念:分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束时,其所属的资源可以回收。一个没有被分离的线程在终止时会保留它的虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸线程”。创建线程时默认是非分离的
如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未释放的系统资源和进程资源,包括保存线程返回值的内存空间、堆栈、保存寄存器的内存空间等。 - 使用方法:如果在创建线程的时候就直到不需要了解线程的终止状态,那么可以修改pthread_attr_t结构体的detachstate属性,让线程以分离状态启动。可以使用pthread_attr_setdetachstate函数来设置线程的分离状态属性。线程的分离属性有两种合法值:
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);//使用pthread_attr_getdetachstate可以获得线程的分离状态属性//设置线程分离属性的步骤//1、定义线程属性变量pthread_attr_t attr//2、初始化attr,pthread_attr_init(&attr)//3、设置线程为分离或非分离 pthread_attr_setdetachstate(&attr, //detachstate)//4、创建线程pthread_create(&tid, &attr, thread_fun, //NULL)//所有的系统都会支持线程的分离状态属性,
实例练习
分别连接属性为非分离的线程,和属性分离的线程,查看链接结果:
#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>int state;pthread_attr_t attr1;pthread_attr_t attr2;void * pthread_func1(void *arg){ //得到线程属性中的分离属性并打印:非分离 pthread_attr_getdetachstate(&attr1,&state); printf("this is pthread1,state = %d\n",state); return NULL;}void * pthread_func2(void *arg){ //得到线程属性中的分离属性并打印:分离 pthread_attr_getdetachstate(&attr2,&state); printf("this is pthread2,state = %d\n",state); return NULL;}int main(){ pthread_t pid1,pid2; int err; //属性初始化 pthread_attr_init(&attr1); pthread_attr_init(&attr2); //设置线程二的属性为分离的,线程一采用默认属性:非分离的 pthread_attr_setdetachstate(&attr2,PTHREAD_CREATE_DETACHED); //创建两个线程,一个默认属性,一个分离属性 pthread_create(&pid1,&attr1,pthread_func1,NULL); pthread_create(&pid2,&attr2,pthread_func2,NULL); //连接两个线程,带有分离属性的线程,线程还回后系统自动释放资源,不需连接,连接会失败 err=pthread_join(pid1,NULL); if(err!=0) printf("join1 failed\n"); else printf("join1 succeed\n"); err=pthread_join(pid2,NULL); if(err!=0) printf("join2 failed\n"); else printf("join2 succeed\n"); //pthread_attr_t结构在使用之前需要初始化,使用完之后需要销毁 pthread_attr_destroy(&attr1); pthread_attr_destroy(&attr2); return 0;}
结果中的0表示:PTHREAD_CREATE_JOINABLE
结果中的1表示:PTHREAD_CREATE_DETACHED
可在/usr/include/pthread.h中查看:
- 线程栈属性:这里不做介绍
3.线程的同步属性(与线程创建时的属性有区别)
- 互斥量的属性
- 就像线程有属性一样,线程的同步互斥量也有属性,比较重要的是进程共享属性和类型属性。互斥量的属性用pthread_mutexattr_t类型的数据表示,当然在使用之前必须进行初始化,使用完成之后需要进行销毁:
互斥量初始化
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
互斥量销毁
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); - 进程共享属性有两种值:
PTHREAD_PROCESS_PRIVATE,这个是默认值,同一个进程中的多个线程访问同一个同步对象
PTHREAD_PROCESS_SHARED, 这个属性可以使互斥量在多个进程中进行同步(在进程中共享,创建共享内存使得进程都能访问),如果互斥量在多进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程。 - 设置互斥量进程共享属性
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);
- 就像线程有属性一样,线程的同步互斥量也有属性,比较重要的是进程共享属性和类型属性。互斥量的属性用pthread_mutexattr_t类型的数据表示,当然在使用之前必须进行初始化,使用完成之后需要进行销毁:
进程共享属性需要检测系统是否支持,可以检测宏_POSIX_THREAD_PROCESS_SHARED
实例练习
用互斥量实现两个进程间的同步。(如果不进行同步的话,两个进程打印的内容是一样的)。
#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>//Link with -lrt,记得编译时连接lrtint main(){ pid_t pid; char * shm_name1="shm_name1"; char * shm_name2="shm_name2"; char * buff; int shm_id1,shm_id2; pthread_mutex_t *mutex; pthread_mutexattr_t mutexattr; //创建共享内存1 shm_id1=shm_open(shm_name1, O_RDWR|O_CREAT,0644); //调整共享内存大小 ftruncate(shm_id1, 100); //映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程 buff =(char *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id1, 0); //创建共享内存2 shm_id2=shm_open(shm_name2, O_RDWR|O_CREAT,0644); //调整共享内存大小 ftruncate(shm_id2, 100); //映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程 mutex =(pthread_mutex_t *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id2, 0); pthread_mutexattr_init(&mutexattr);//初始化属性 #ifdef _POSIX_THREAD_PROCESS_SHARED//判断系统是否支持设置属性 pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);//设置属性 #endif pthread_mutex_init(mutex,&mutexattr);//初始化互斥量 pid=fork();//创建新进程 if(pid<0) { printf("fork child proccess failed\n"); return -1; } if(pid==0) { sleep(1); pthread_mutex_lock(mutex); strcpy(buff,"world"); pthread_mutex_unlock(mutex); printf("i am child and buff is :%s\n",buff); } if(pid>0) { pthread_mutex_lock(mutex); strcpy(buff,"hello"); sleep(2); pthread_mutex_unlock(mutex); printf("i am parents and buff is :%s\n",buff); } sleep(1); //销毁互斥量,以及属性 pthread_mutex_destroy(mutex);//这里先销毁,然后再解除映射,相反的话,映射先解除了,销毁是就找不到,会发送段错误 pthread_mutexattr_destroy(&mutexattr); //销毁共享内存 munmap(buff, 100); //解除映射 shm_unlink(shm_name1); //销毁共享内存 munmap(mutex, 100); //解除映射 shm_unlink(shm_name2); return 0;}
解析:父进程先运行(子进程有进行睡眠),并加锁互斥锁,然后改变共享内存中的内容,父进程睡眠->子进程运行,子进程加互斥锁阻塞,父进程睡眠结束—>父进程运行解锁并打印共享内存的内容,—>子进程加锁,改变共享内存内容,加锁打印。因此父子进程打印的内容是不一样的。如果没有进行互斥量的加锁同步,打印的内容是一样的。
- 读写锁的属性:与互斥锁类似。只是具体实现函数不同。
- 条件变量的属性:与互斥锁类似。只是具体实现函数不同。
- linux线程学习(5)
- linux 线程学习(一)
- linux线程学习(1)
- linux线程学习(2)
- linux线程学习(3)
- linux线程学习(4)
- linux线程学习(6)
- Linux线程入门学习(一)-认识线程
- linux学习笔记-线程(1)
- linux 线程学习(二)属性设置
- linux多线程学习(一)---线程基础
- Linux学习(二十一):线程
- linux 线程学习
- linux C 线程学习
- linux线程学习
- Linux线程学习总结
- Linux线程学习笔记
- Linux 线程学习
- windows 提权
- HDU 1402 A * B Problem Plus
- 基数排序
- hbase-region预分区(pre-splitting)
- gson笔记
- linux线程学习(5)
- python 奇异值分解小程序 分类:机器学习
- 《JavaScript高级程序设计》总结 第四章 变量、作用域和内存问题
- 一张图揭秘Java自学和培训的区别
- 程序小白---关于封装、继承、抽象类和接口的一些理解。
- 用 cctld工具创建带有国家代码的IP地址表
- UART学习笔记
- 【顶部导航】Android自定义指示器实现顶部导航(三角形,线,bitmap指示器),Fragment与ViewPager的组合。
- C语言 贪吃蛇实现(不闪屏)