博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程_上
阅读量:2242 次
发布时间:2019-05-09

本文共 3042 字,大约阅读时间需要 10 分钟。

线程

简介

简单来说线程就是进程的一个分支,在进程内部运行,这个被创建出来的线程在Linux系统下可以看作是一个轻量级的进程(LWP),一旦一个轻量级进程被创建出来,他与父进程共用同一份代码和数据,为什么说是轻量级进程呢,要知道在Linux系统下,并没有描述线程的这一结构体,所以并没有真正意义上的线程,有的只是用进程模拟出来的线程,为什么说模拟出来的,因为他有自己的PCB,但他不会拷贝父进程的地址空间和页表,而是在父进程的地址空间内部运行。所以可以想来,他与父进程公用一份代码和数据,这样设置的好处是让线程的运行更加流畅,移植性更好。

了解了以上Linux平台下对线程介绍后,我们发现,所有PCB代表的进程都可以称之为轻量级进程(有可能该进程只是一个进程),进程变为承担分配系统资源的基本实体,而线程是CPU/操作系统调度的基本单位(存在多个执行流时才需要调度)。

呢为什么既要有进程,又要有线程呢? 
设置进程为的是强调资源的独享,而设置线城市为了强调资源的共享。 
一个进程中的多个线程共享什么资源呢

  1. 文件描述符表
  2. 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
  3. 当前公作目录
  4. 用户id和组id

但线程也需要自己的自己的自由信息,例如在线程中产生的临时变量。这些临时变量存放在每个线程专属的私有栈之中,除了这些临时变量但有些资源是每个线程各有1份的: 

  1. 线程id
  2. 上下文,包括各种寄存器的值、程序计数器和栈指针
  3. 栈空间
  4. errno变量
  5. 信号屏蔽字
  6. 调度优先级

注意线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。在Linux 
上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项

线程创建

返回值:成功返回0,失败返回错误号。错误信号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有1个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

在多个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,当新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为void ,这个指针按什么类型解释由调度者自己定义。start_routine的返回值类型也是void ,这个指针的含义同样由调度者自己定义。start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值,类似于父进程调用wait(2)得到子进程的退出状态。 
pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。我们知道进程id的类型是pid_t,每个进程的id在整个系统中是唯一的,调用getpid(2)可以获得当前进程的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数用printf打印,调用pthread_self(3)可以获得当前线程的id,attr参数表示线程属性。

线程等待

返回值:成功返回0,失败返回错误号 
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ptr所指向的单元内存放的是thread线程函数的返回值
  2. 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元内存放的是常数PTHREAD_CANCELED。(常数PTHREAD_CANCELED的值是-1。可以在头文件pthread.h中找到它的定义)
  3. 如果thread线程是由于调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
#include 
#include
#include
void* thread1(void* _val) {
printf("thrad 1 returning...\n"); return (void*)1; } void* thread2(void* _val) {
printf("thread 2 returning...\n"); pthread_exit((void*)2); } void* thread3(void* _val) {
while(1) {
printf("pthread 3 is runing\n"); sleep(1); } return NULL; } int main() {
pthread_t tid; void* tret; //thread_join pthread_create(&tid, NULL, thread1,NULL); pthread_join(tid, &tret); printf("thread1 return, thread id is : %u, return code is : %d\n", tid, (int)tret); //thread_exit pthread_create(&tid, NULL, thread2,NULL); pthread_join(tid, &tret); printf("thread2 return, thread id is : %u, exit code is : %d\n", tid, (int)tret); //thread 3 cancel by main pthread_create(&tid, NULL, thread3,NULL);

retvalvoid *类型,和线程函数返回值的用法一样,其它线程可以调用pthread_join获得这个指针。 
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了,这意味着函数内部所有临时变量已经销毁。

线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适合,从main函数return相当于调用exit
  2. 单个线程可以调用pthread_cancel终止同一进程中的另一个线程
  3. 线程可以调用pthread_exit终止自己

运行结果:

你可能感兴趣的文章
java比较日期大小及日期与字符串的转换【SimpleDateFormat操作实例】
查看>>
Oracle新表使用序列(sequence)作为插入值,初始值不是第一个,oraclesequence
查看>>
java中System.exit()方法
查看>>
在hbase shell中过滤器的简单使用
查看>>
java静态方法和实例方法
查看>>
java多线程并发去调用一个类的静态方法,会有问题吗?
查看>>
关于JAVA中的static方法、并发问题以及JAVA运行时内存模型
查看>>
Java命令学习系列(一)——Jps
查看>>
java如何计算程序运行时间
查看>>
Java Calendar 类的时间操作
查看>>
Java]NIO:使用Channel、Charset(字符集)、使用Charset传递CharBuffer
查看>>
Eclipse下运行Maven项目提示缺少maven-resources-plugin:2.4.3
查看>>
Java 中int、String的类型转换
查看>>
比较两个JSON字符串是否完全相等
查看>>
删除JSONArray中的某个元素
查看>>
Linux下Tomcat重新启动
查看>>
使用HttpClient请求另一个项目接口获取内容
查看>>
HttpClient get和HttpClient Post请求的方式获取服务器的返回数据
查看>>
net.sf.json Maven依赖配置
查看>>
Could not initialize class net.sf.json.JsonConfig错误解决
查看>>