Linux-线程学习(上)
发布时间:2022-11-17 11:19:32 所属栏目:Linux 来源:
导读: 本文导航:
内容所占百分比
线程概念
40%
线程与进程区别与联系
20%
线程优缺点
10%
线程控制(创建,终止,等待)
30%
线程
内容所占百分比
线程概念
40%
线程与进程区别与联系
20%
线程优缺点
10%
线程控制(创建,终止,等待)
30%
线程
|
本文导航: 内容所占百分比 线程概念 40% 线程与进程区别与联系 20% 线程优缺点 10% 线程控制(创建,终止,等待) 30% 线程的概念 谈到线程,我们先从进程说起。 我们写的程序从硬盘加载到内存开始运行时,进程就产生了。也就是操作系统开始为这个程序创建PCB,分配系统资源,比如分配一块虚拟地址空间,一个页表,一块物理内存。当这个进程内部有多个执行流时,现在我们可以简单理解父进程用vfork()创建了多个子进程时,这些子进程也需要资源。如果能分得资源就会独自分的资源,但如果分不了就共享。共享一块地址空间。共享一个页表和一块物理内存。 为了便于来理解,我画了一张简明图 有3个PCB,在以前的认知里,我们叫做3个进程。 现在我们需要引入线程的概念。 线程概念 进程到线程 这里写图片描述 在明确线程概念后,我们就要站在线程的角度去理解上面那张图了。 图中3个PCB ,我们就要把他们称之为3个线程了。因为这是在进程里的3个执行控制流。他们共享一块虚拟地址空间。一块页表,所以这3个线程所看到的物理类存也是一样的。但是他们可以执行各自的任务,并有自己生命特征。 所以在图中,进程就是创建执行代码,创建PCB,申请资源,分配资源的实体,即整个图。而线程就是那些单个PCB,在线程中称之为TCB。 或许下面这张图有助于来理解。 这里写图片描述 线程特点 其中最重要的数据是栈和寄存器。私有栈是为了保存临时变量,便于函数调用等操作。私有寄存器是为了方便线程切换,保存上下文。 这里写图片描述 进程的多个线程共享 .同地址空间,因此Test Segment,Data Segment都是共享的,如果定义个函数雇各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境: 进程与线程联系 在Linux下并没有专门为线程设计这么一个概念,也就是说没有真正意义上的线程,它在linux下是由进程模拟的,可以认为所有的PCB都可以称为轻量级进程(不一定是进程,也可能是线程)。进程是分配系统资源的一个实体,而线程是CPU调度的基本单位。 在单个程序中同时运行多个线程完成不同的工作,称为多线程。相对来说,多进程相对稳定,多线程相对不稳定。可以认为,进程是分配系统资源的一个实体,而线程是CPU调度的基本单位。 进程具有独立的地址空间,而线程并没有,同一进程内部的线程共享进程的地址空间。 由于Linx下没有真正的线程,Linux用进程来描述线程和组织线程。所以在Linux下只有轻量级的线程,操作系统是看不到线程的TCB的。所以下图中的 第四个图:多个进程多个线程图中,操作系统可以看到的是6个PCB, 线程的优点 线程的缺点 线程控制 POSIX线程库 与线程有关的函数构成了一个完整的系列,绝?大多数函数的名字都是以“pthread_”打头的 要使用这些函数库,要通过引入头文件, 链接这些线程函数库时要使用编译器命令的“-lpthread”选项 创建线程 调用函数 返回值:成功返回0,失败返回 错误码 错误检查: 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。 pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回 pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小 测试用例 注意:编译链接时,一定要加上-lpthreead选项,见图中命令部分。因为这是用户级别的库,并不是系统提供的库。 #include #include #include //新线程的例程(执行任务,打印字符串) void * my_run( void *arg) { while(1) { printf( " i am %s\n",( char *)arg); sleep( 1); } } int main( ) { //申明一个本地变量tid,用来保存对等线程的ID pthread_t tid; //调用pthread_create函数创建一个新的线程, pthread_create(&tid,NULL,my_run,"newthread"); //调用结束,同时运行,并且tid包含了新线程的id while(1) { printf( " i am main thread\n"); sleep(2); } return 0; } pthread_create创建成功! 主线程和新线程各自打印不同的内容。 正常情况下,一个程序里是不会同时执行两个死循环的。但是,在这个程序里,居然可以同时执行两个死循环。原因就在于这时两个线程在运行。各自执行不同的任务,有自己的栈空间和寄存器,支持上下文切换和函数调用。 终止线程 如果需要只终止某个线程而不终止整个进程,可以有以下方法: void * my_run( void *arg) { while(1) { printf( " i am %s\n",( char *)arg); sleep( 1); return NULL; } } 这里写图片描述 新线程终止,主线程继续执行。 - 线程可以调用pthread_ exit终止自己。 pthread exit函数 功能:线程终止 #include void pthread_exit(void *retval); 参数 value ptr:value ptr不要指向一个局部变量。 返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身) #include #include #include #include void * my_run( void *arg) { printf("thread 1 returning ....\n"); int *p=(int *)malloc(sizeof(int)); *p=1; pthread_exit((void *)p); } int main( ) { pthread_t tid; void *ret; //thread 1 exit pthread_create(&tid,NULL,my_run,NULL); //等待新线程退出,退出码保存在ret中 pthread_join(tid,&ret); printf("thread return,id is:%ld,return code:%d\n",tid,*(int *)ret); free(ret); while(1) { printf("i am main thread\n"); sleep(2); } return 0; } ,pthread exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。 调用phread_exit(),他会等待所有其他对等线程(就是同一个进程中的除自身外其他线程)终止,然后再终止主线程和整个进程。 如果某个对等线程调用linux的exit函数,则该函数终止进程以及所有与该进程相关的线程 pthread_cancel函数 功能:取消一个执行中的线程 #include int pthread_cancel(pthread_t thread); 参数 thread:线程ID 返回值:成功返回0;失败返回错误码 void* my_run_2(void* arg) { while(1) { printf( "thread 2 is running...\n"); sleep(1); } return NULL; } int main( ) { pthread_t tid; void *ret; pthread_create(&tid,NULL,my_run_2,NULL); sleep(1); //取消执行中的线程2 pthread_cancel(tid); //等待线程2的返回状况,获取返回值 pthread_join(tid,&ret); if(ret==PTHREAD_CANCELED) { printf("thread return,id is:%ld,return code:PTHREAD_CANCELED\n",tid); } else { printf("thread return,id is:%ld,return code:NULL\n",tid); } return 0; } 介绍一个获取当前线程id的函数 #include pthread_t pthread_self(void); 获取当前文件中线程tid命令 ps -eLf | head -1&& ps -eLf | grep a.out 以上都是显示终止,另外还有隐式终止。即:顶层的线程调用返回时,线程会隐式终止。 线程等待 为什么需要线程等待? 已经退出的线程线程池linux,其空间没有被释放,仍然在进程的地址空间内。 创建新的线程不会复?用刚才退出线程的地址空间。 功能:等待线程结束原型 int pthread_join(pthread_t thread, void **value ptr); 参数 thread:线程ID value_ptr:它指向一个指针,后者指向线程的返回值返回值:成功返回0;失败返回错误码 调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下: 如果thread线程通过return返回,value ptr所指向的单元?里存放的是thread线程函数的返回值。 如果thread线程被别的线程调?用pthread cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD CANCELED。 如果thread线程是自调用pthreadexit终止的,valueptr所指向的单元存放的是传给pthread_exit的参数。 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。 测试用例可以参考上面的代码。 线程分离 线程分离接口 #include int pthread_detach(pthread_t thread); #include int pthread_detach(pthread_t self()); 返回值:都是成功返回0,失败返回-1. 线程被分离后,就不能在进行pthread_join()操作。否则会出错。因为分离后的线程资源自动就被回收了,再进行等待回收资源。必定导致等待失败。 测试用例如下 #include #include #include #include void * thread_run( void *arg) { //新分离线程自我分离 pthread_detach(pthread_self( )); printf("%s\n",( char *)arg); return NULL; } int main( ) { pthread_t tid; if( pthread_create(&tid,NULL,thread_run,"thread1 run...")!=0) { printf("create thread error\n"); return 1; } //主线程分离新线程 //pthread_detach(tid); int ret=0; sleep(1); if( pthread_join( tid,NULL)==0) { printf("wait thread success\n"); ret=0; } else { printf("wait thread failed\n"); ret= 1; } return ret; } (编辑:我爱制作网_沈阳站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐


浙公网安备 33038102330576号