Linux环境下编程(二)——线程基础概念
来源:互联网 发布:女生休闲鞋推荐 知乎 编辑:程序博客网 时间:2024/09/21 06:19
上一篇讲了进程的基本概念,这一篇讲线程的。
基础概念
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
经常被问到的一个问题是:线程和进程有什么区别?为什么要有线程这样一个东西?
线程是运行在进程上下文中的逻辑流,最开始的时候一个进程对应一个主线程。后来觉得既然好多资源是可以共享的,为什么每次都要创建一个新的进程?不断地去切换进程的上下文造成了很多不必要的开销。既然有的资源可以共享,那么我们干脆弄一个轻量级的进程,这样切换起来岂不是快得多?而且现在操作系统都是多核的,既然它有几个脑子,那么每个脑子绑定一个线程,然后共同支配一个身体,岂不是更快?(当然了这段话说的并不是太严谨,仅供理解)。
同一进程里的线程,哪些资源是独占的,哪些资源是共享的?
独有的:线程ID、栈、栈指针、程序计数器、条件码、通用目的寄存器。
共享的:除了上述外,进程虚拟地址里的其它资源。
下面讲讲具体的应用
Posix线程是在C语言线程的标准接口。
一个线程对应一个线程id,它的类型是pthread_t,是一个自然数,但是这个并不是int型,而是无符号长整型,打印的时候要用%lu。
线程获取自身id的函数为:
#include <pthread.h>pthread_t pthread_self(void);
线程的创建接口为:
#include<pthread.h>typedef void* (func)(void *);int pthread_create(pthread_t* tid, pthread_attr_t* attr,func* f, void* arg)
pthread_create和进程的创建函数有些区别,它的返回值不是线程的id,而是返回0或者非0,0代表创建成功,非0代表创建失败。
我们来看它的参数列表,第一个参数tid,就是我们传入的tid的地址,然后函数结束后,tid的值将会变成子进程的id。
第二个参数为线程创建的一些选项,默认是NULL。
第三个参数为一个函数地址,func*为函数指针,线程就从这个地址开始执行。
第四个参数为传递给线程函数的参数,如果线程函数需要多个参数的话,那么就把它作为结构体的形式传入。
下面来看一个具体实例:
#include<pthread.h>#include<stdio.h>#include<stdlib.h>typedef void* (func)(void *);void error_msg(int ret, char *msg){//错误处理 printf("%s: %d", msg, strerror(ret));}void Pthread_create(pthread_t* tid, pthread_attr_t* attr,func* f, void* arg){//包裹函数 int ret; if ( (ret = pthread_create(tid, attr,f , NULL )) != 0){ error_msg(ret, "create pthread faild"); exit(0); }}void* pthread_instance(void* args){//线程实例 printf("Hello, I am a pthread\n, my tid is %lu\n", pthread_self()); return NULL;}int main(void){ pthread_t tid; Pthread_create(&tid, NULL, pthread_instance, NULL);//创建线程 printf("my child tid is %lu, my tid is %lu\n", tid, pthread_self() ); sleep(1);//让主线程慢点执行 exit(0); return 0;}
运行结果:
结果分析:
1、在用gcc编译的时候,需要在后面加上动态链接库
2、为什么程序的倒数第三行需要加上sleep(1)?假如我把sleep(1)给注释掉,会出现什么情况?
注释掉sleep(1)
运行结果:
我们发现,子线程(也可以叫对等线程)的printf语句并没有被打印出来,这个到底是为什么?
其实,在主线程中的exit语句不仅将主线程退出了,更重要的是它终止了进程,当进程没有了,依赖它的子线程自然就不能够正常运行,所以那句话没有打出来。
如果我们加上了sleep(1)主线程休眠了1下,这个时候子线程先正常执行完毕,然后接着主线程也执行完毕,所以会打印两个printf出来。
对等线程(就是主线程创建的线程)的回收
很显然,上面的sleep(1)造成了一个问题,就是很多时候我们根本不知道主线程和对等线程的运行时间,只是通过sleep人为地去判断时间,是不是有点不妥当?
这个时候我们有两个方法来处理:
1、采用pthread_join函数,
#include<pthread.h>int pthread_join(pthread_t tid, void **thread_return);成功则返回0,不成功则返回非0.
线程通过调用phread_join 来等待其它线程终止,pthread_join函数会阻塞,直到tid终止,将线程调用返回的(void*)指针赋值为thread_return指向的位置,然后回收已终止线程占用的所有存储器资源。
具体实验如下:
et, char *msg){ printf("%s: %d", msg, strerror(ret));}void Pthread_create(pthread_t* tid, pthread_attr_t* attr,func* f, void* arg){ int ret; if ( (ret = pthread_create(tid, attr,f , NULL )) != 0){ error_msg(ret, "create pthread faild"); exit(0); }}void Pthread_join(pthread_t tid, void **thread_return){ int ret; if ( (ret = pthread_join(tid, thread_return)) != 0){ error_msg(ret, "join faild"); exit(0); }}void* pthread_instance(void* args){ printf("Hello, I am a pthread, my tid is %lu\n", pthread_self()); return NULL;}int main(void){ pthread_t tid; Pthread_create(&tid, NULL, pthread_instance, NULL); printf("my child tid is %lu, my tid is %lu\n", tid, pthread_self() ); Pthread_join(tid, NULL); exit(0); //pthread_detach(tid); //pthread_exit(NULL); return 0;}
运行结果:
结果分析:
1、是不是和上面结果一样?主线程回收了pthread_join等待的对等线程资源后才退出。
2、大家注意到了我下面注释掉的两行代码没有?
线程的状态分为可结合的(joinable)和可分离的(detached)。一个可结合的线程可以被其它的线程终止和杀死,在被其它线程回收之前,它的资源不会被释放;一个可分离的线程,它不能够被其它的线程终止或释放,它的资源会在线程结束后由系统自动释放。
分离一个进程的函数为下,成功返回0,出错返回非0。
#include<pthread.h>int pthread_detach(pthread_t tid);
那么,有什么办法能够让主线程在终止的时候先等待对等线程终止呢?有的,这个就是pthread_exit干的事情了。
pthread_exit函数,若成功则返回0,出错返回非0。
#include<pthread.h>phread_exit(void *thread_return);
大家可以试试,是不是把Pthread_join注释掉,然后采用pthread_detach和pthread_exit之后结果一样。
- Linux环境下编程(二)——线程基础概念
- Linux环境下编程(二)——线程的同步
- linux环境下C编程(二)
- Linux编程基础之多线程编程(二)
- linux下bluetooth编程(一)基础概念
- linux下bluetooth编程(一)基础概念
- 多线程编程之Linux环境下的多线程(二)——好文
- UNIX环境高级编程——网络基础概念
- linux学习笔记二(linux下c编程基础)
- linux基础——linux下线程池的编程实现及注意事项
- Linux系统编程——线程函数(二)
- Linux多线程编程(二)——线程属性
- UNIX环境编程学习笔记(27)——多线程编程(二):控制线程属性
- Linux 线程概念与基础
- linux网络编程(一)——程序、进程和线程的概念
- Linux系统编程---线程概念
- Linux——网络编程基础(二)
- Linux环境下的工程管理器—make(二)
- HDU 4548 美素数 // 素数筛法
- 逗号表达式
- storm网上中文资料搜集大全
- spark-1.4配置
- 基于BP弱分类器用Adaboost的强分类器
- Linux环境下编程(二)——线程基础概念
- C# ArrayBuffer[转]
- 用Fluentd实现收集日志到HDFS(下)
- tcp数据包的标志位
- 黑马程序员——Java基础——数组
- 利用SwingWorker异步加载图片
- "\n" 与 '\n' 是否等价
- Junit4 单元测试的断言
- [HDOJ 4551] 生日猜猜猜