(Linux)线程基础

本文介绍了Linux线程的概念,包括线程与进程的区别、线程的优缺点、线程的创建与控制。Linux通过轻量级进程模拟线程,线程作为调度的基本单位,具有创建成本低、调度成本低等优点,但也存在性能损失和健壮性降低等问题。线程控制涉及POSIX线程库,如pthread_create、pthread_exit和pthread_cancel等函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是线程

  1. 线程是比进程的执行粒度更细的一个执行流(线程是进程的子集),是进程内部的执行分支(可理解为线程是在进程的地址空间内运行),是调度的基本单位。
  2. 一个进程至少有一个执行线程
  3. Linux没有真正的线程,但它用进程来模拟实现线程,而这种进程被称为轻量级进程。
  4. 线程用TCB管理,但是Linux下没有TCB,因此Linux用PCB模拟TCB,创建进程只有创建PCB,再分配资源即可,并且cup认为PCB为调度的基本单位。

线程和进程的比较

  1. 进程时资源竞争的基本单位
  2. 线程是程序执行的最小单位
  3. 线程共享同一进程数据,但也拥有自己独立的一部分数据,如自己的上下文数据(主要用于恢复数据)、线程ID、私有结构栈、寄存器、errno、信号屏蔽字、调度优先级,所以线程相对于进程而言,创建和调度成本低。
  4. 一个进程的多个线程共享以下数据:同一个地址空间、文件描述符表、每种信号的处理方式、当前工作目录、用户ID和组ID,一个进程只有一个主线程

线程的优点

  1. 创建成本低。
  2. 调度(选择、切换)成本低。
  3. 占用资源少。
  4. 能充分利用多处理器的可并行数量。
  5. 在等待慢速I/O操作结束的同时,程序可执行其他的计算机任务。
  6. 计算密集型应用的时候,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  7. 线程可以同时等待不同I/O操作。

线程的缺点

  1. 性能损失(主要体现在计算密集型线程,由于该线程不可和其他线程公用同一个处理器,所以当该线程数量超过处理器数量的时候,就会增加额外的同步和调度开销,而可用的资源不变)
  2. 健壮性降低(线程和线程间缺乏安全性)
  3. 缺乏访问控制(也就是一个线程在调用某些OS函数时,会影响整个进程,更通俗一点就是,一个线程的某些操作会影响整个进程,特别是当一个线程出错的时候,整个进程就会被释放)
  4. 编辑难度提高

线程控制

POSI线程库

  • 和线程有关的函数构成可一个完整的系列,一般都以pthread_ 开头。
  • 链接这些线程函数时需要使用编译器命令的“-lpthread”选项。
  • 头文件”pthread.h”

创建线程

int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*) void* arg);
//thread:返回线程ID,ptread_t是无符号长整型
//attr:线程属性,一般设默认NULL
//start_routine:函数指针,表示线程要执行的函数
//arg:传个线程启动函数的参数,一般为NULL
//返回值:成功返回0,失败返回错误码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>


void* rout(void* arg){//必须要交上参数,否则会报错,并且编译时一定要加上-lpthread选项                                                                                                          
    (void)arg;
    for(;;){
        printf("I an thread1!\n");
        sleep(2);
    }   
}

int main(){
    pthread_t tid;
    int ret;
    if((ret = pthread_create(&tid, NULL, rout, NULL)) != 0){ 
        printf("pthread_create error, error : %s\n", strerror(ret));
        exit(EXIT_FAILURE);
    }   

    for(;;){
        printf("I am main thread\n");
        sleep(1);
    }   
    return 0;
}

线程ID

线程ID是pid_t类型的变量,而且用来唯一标识线程的一个整型变量。
Linux提供了gettid系统调用来返回其线程ID,但是gilbc并没有将该系统调用封装起来,在开放接口来供程序员使用,如果想要获得线程ID,可以使用如下方法:

#include <sys/syscall.h>
pid_t tid;
tid = syscall(SYS_gettid);

pthread_create函数会产生一个线程ID,存放在第一个参数指向的地址中,该线程ID和前面说的线程ID不是一回事,前面的线程ID属于进程调度范围,因为线程是轻量级进程,是操作系统的调度器的最小单位,所以需要一个唯一的值来表示该线程。
线程库NPTL提供了pthread_self函数来获得线程自身的ID

pthread_t pthread_self(void);

线程终止

止终止线程而不终止整个进程可以使用以下三个方法:

  1. 从线程函数处return。这种方法对主线程不适用,因为从main函数return相当于调用exit。
  2. 线程可以调用pthread_exit来终止自己。
  3. 一个线程可以调用pthread_cancle终止同一个进程中的另外一个线程。
    pthread_exit函数:线程终止
void pthread_exit(void* value_ptr);
//value_ptr:不要将其指向一个局部变量
//返回值:无返回值,线程结束的时候无法返回到它调用者(自身)

注意:pthread_exit或者return返回指针所指向的内存单元必须是全局的或者使用malloc分配的,不能再线程函数栈上分配,因为当其他线程得到这个返回指针的时候线程函数已经退出了。

pthread_cancel函数:取消一个执行中的线程

int pthread_cancel(pthread_t thread);
//thread:线程ID
//返回值:成功放会0,失败返回错误码

进程等待

为什么?

  1. 已经退出的线程,其空间并没有被释放,仍然在进程的地址空间内。
  2. 创建新的线程不会复用刚才退出线程的地址空间。

因此,就需要等待线程真正终止。

方法:

int pthread_join(pthread_t thread, void** value_ptr);
返回值:成功返回0,失败返回错误码

thread线程以不同的方法终止,通过pthread_join的到的终止状态也有所不同:

  • 通过return返回,value_ptr所指向的单元内存放的是thread线程函数的返回值
  • thread线程被别的线程调用pthread_cancel异常终止,value_ptr指向的单元里存放的是常熟PTHREAD_CANCELED
  • 被自己调用pthread_exit终止,其指向的单元存放的是传给pthread_exit的参数
  • 如果对其不感兴趣,就传NULL给value_ptr

分离线程

为神马:

  • 默认情况下,新创建的线程在线程退出后,需要进行pthread_join操作,否则无法释放资源,从而造成内存泄漏
  • 如果不关心线程的返回值,join就是一种负担, 这时,我们就可以让操作系统,当线程退出时,自动释放资源。

进程内任意分离的线程出现异常,同样会影响到进程

//用于线程分离
int pthread_detach(pthread_t thread);

测试代码:

  #include <stdio.h>                                                            
  #include <unistd.h>
  #include <pthread.h>
  void* thread_run1(char* arg){
      printf("I am %s\n", arg);
      pthread_detach(pthread_self());    
      pthread_exit(0);
      //return (void*)123;
  }

  void* thread_run2(char* arg){
      printf("I am %s\n", arg);
      return (void*)123;
  }

  int main(){
      pthread_t tid1, tid2;
      pthread_create(&tid1, NULL, thread_run1, "thread 1");
      pthread_create(&tid2, NULL, thread_run2, "thread 2");
     // pthread_cancel(tid);

      void* ret1;
      void* ret2;

      pthread_join(tid1, &ret1);
      pthread_join(tid2, &ret2);


      printf("%lu  %lu\n", pthread_self(), tid1);
      printf("main thread run new thread ret:%lu\n", (size_t)ret1);
      return 0;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值