菜鸟笔记
提升您的技术认知

操作系统笔记

进程,线程,协程与并行,并发进程线程协程的区别死锁进程,线程,多线程i++的线程安全性同步和异步孤儿进程和僵尸进程/proc进程信息linux中的分段和分页互斥量 mutex线程进程间通信进程创建进程优先级进程的基础知识进程与线程的区别(面试题)线程的控制(创建,终止,等待,分离)可重入 VS 线程安全死锁的概念一级缓存和二级缓存的理解一句话解说内存屏障 Memory barrierbrk(), sbrk() 用法详解malloc/free函数的简单实现一文讲透 “进程、线程、协程”Linux进程状态线程池的陷阱linux内核学习之进程和线程进程与线程的区别和联系内存寻址linux IO子系统和文件系统读写流程Page cache和buffer cache的区别与联系漫谈linux文件IO多线程和多进程的区别内存泄漏字节、字、位、比特的概念和关系如何避免死锁ANSI是什么编码?CPU寻址范围(寻址空间)CPU 使用率低高负载的原因创建多少个线程合适操作系统下spinlock锁解析、模拟及损耗分析线程堆栈堆和栈的内存分配堆和栈的概念和区别堆和栈的区别,申请方式,程序的内存分配什么是 POD 数据类型Linux内存分配小结--malloc、brk、mmap系统调用与内存管理(sbrk、brk、mmap、munmap)进程描述和控制CPU执行程序的原理编译的基本概念Linux虚拟地址空间布局一个程序从源代码到可执行程序的过程程序的运行机制——CPU、内存、指令的那些事分页内存管理——虚拟地址到物理地址的转换深刻理解Linux进程间通信fork之后父子进程的内存关系fork之后,子进程继承了父进程哪些内容关于协程及其锁的一些认识对协程的一点理解std::thread join和detach区别CAS和ABA问题CAS算法锁和无锁无锁队列的实现Lock-Free 编程锁开销优化以及CAS进程、线程和协程之间的区别和联系多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)Linux 原来是这么管理内存的线程上下文切换怎么玩儿进程和线程通信原理CPU密集型 和 IO密集型cas原理以及Atomic原子类分析改变线程状态的八个方法六种进程间通信方式

线程

阅读 : 1979

什么是线程

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行
  • 线程本质是在进程地址空间内运行
  • Linux没有真正的线程,是用进程模拟的
  • 线程强调资源共享
  • 线程是调度的基本单位

进程VS线程

  • 进程是资源分配的基本单位

  • 线程是调度的基本单位

  • 每个线程的硬件都有自己的独立的上下文数据

  • 线程共享进程数据,但也拥有自己的一部分数据

    • 线程ID
    • 一组寄存器
    • errno
    • 信号屏蔽字
    • 调度优先级
进程的多个线程共享同一地址空间,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,哥线程还共享以下进程资源和环境:
  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前的工作目录
  • 用户id和组id

让我们画一个进程与线程的关系图:

线程控制

POSIX线程库

  • 与线程有关的函数构成一个完整的系列,绝大多数函数的名字都是以“pthread_”开头的
  • 要使用这些线程库,要引入头文件<pthread.h>
  • 链接这些第三方库,编译器命令是“-lpthread”选项

1. 线程的创建

功能:创建一个新的线程
原型:
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void*), void *arg); 
参数:
    thread:返回线程ID    
    attr:设置线程的属性,attr为NULL表示使用默认属性    
    start_routine:是个函数地址,线程启动后要执行的函数    
    arg:传给线程启动函数的参数 
返回值:成功返回0;失败返回错误码

错误检查:

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返 回值返回
  • pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通 过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小

让我们写一个代码来用一下这个线程:

  #include<stdlib.h>
  #include<stdio.h>
  #include<string.h>
  #include<pthread.h>
  #include<unistd.h>
  void *rout(void *arg)
  { 
    int i;
    for(; ;)
    { 
      printf("i am thread 1\n");
      sleep(1);
    }
  }
  
  int main()
  { 
    pthread_t tid;
    int ret;
    if((ret = pthread_create(&tid, NULL, rout, NULL) != 0))
    { 
      fprintf(stderr, "pthresd_create: %s\n", strerror(ret));
      exit(EXIT_FAILURE);
    }
                                                                                                                                            
    int i;    
    for(;;)    
    {     
      printf("i am main thread");    
      sleep(1);    
    }    
  }    

2. 线程终止

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

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
功能:线程终止 
原型
    void pthread_exit(void *value_ptr); 
参数
    value_ptr:value_ptr不要指向一个局部变量。 
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身

3. 线程等待 为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内
  • 创建新的线程不会复用刚才线程退出的地址空间
功能:等待线程结束 
原型    
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线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  • 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

4. 分离线程

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程的资源。
int pthread_detach(pthread_t thread);