首页 > 其他分享 >pthread线程

pthread线程

时间:2024-08-25 16:52:01浏览次数:8  
标签:函数 int void 线程 pthread 进程

线程


概念

线程是轻量级进程,一般是一个进程中的多个任务。
进程是系统中最小的资源分配单位

进程 是操作系统中资源分配的最小单位。每个进程都有自己的地址空间,并且拥有独立的资源(如内存、文件句柄等)

进程之间通常是相互独立的,彼此不能直接访问对方的内存空间

线程是系统中最小的执行单位

线程 是操作系统中程序执行的最小单位,也称为“轻量级进程”。一个进程可以包含一个或多个线程,这些线程共享该进程的资源(如内存和文件句柄),但每个线程有自己的栈、寄存器和程序计数器

进程是资源分配的基本单元,而线程是调度和执行的基本单元。线程能够比进程更轻量级地执行任务,因为它们共享进程的资源。

特征

共享资源

线程共享进程的资源,如内存、文件句柄等。多个线程可以同时访问相同的资源,因此它们能够轻松地进行数据交换和通信,而不需要像进程之间那样复杂的IPC(进程间通信)机制。

效率高  

30%(并发度)

上下文切换开销小:线程之间的上下文切换(如切换栈、寄存器)比进程之间的上下文切换要轻量得多,因为线程共享进程的资源。

并发执行:在多核处理器上,多个线程可以真正并发地运行,利用系统资源更加充分。一般来说,通过多线程可以提高系统的并发度,并提升性能。

三方库

pthread  clone   posix

在C语言中,常用的线程库包括 pthreadclone 系统调用和 POSIX 标准库(其中 pthread 是最常用的)
3.1 编写代码头文件: pthread.h
3.2 编译代码加载库: -lpthread   library 

在编译使用 pthread 的程序时,需要链接 pthread

libpthread.so
gcc 1.c -lpthread 

设置gcc的别名
  • 临时别名设置

使用 alias 命令来设置 GCC 的别名

alias gcc='gcc -g -pthread '

这个设置会立即生效,但只在当前的终端会话中有效。如果关闭终端或重启系统,别名设置会消失。

  • 永久别名设置

为了使别名设置在每次终端启动时都有效,将别名添加到用户的 .bashrc 文件中,这样每次登录时,别名都会自动加载:

cd ~ # 切换到家目录

vim .bashrc # 编辑 .bashrc 文件

.bashrc 文件末尾添加以下内容:

alias gcc='gcc -g -pthread '

保存并退出 Vim:

:wq

然后,执行以下命令以重新加载 .bashrc,使别名立即生效:

source ~/.bashrc

这样设置后,每次新打开的终端都会自动应用设置的别名。

线程与进程对比

优点

比多进程节省资源,可以共享变量。


缺点

  • 稳定性

线程和进程相比,稳定性,稍微差些

线程共享进程的地址空间,如果一个线程发生错误(例如访问非法内存),可能会导致整个进程崩溃。这是因为线程之间没有内存隔离,因此相对于多进程模式,线程的稳定性稍差。

线程的调度依赖于操作系统的线程调度器,当系统负载过高时,线程之间的竞争可能导致线程饥饿或优先级反转等问题。

  • 调试难度

线程的调试gdb,相对麻烦些
info thread 查看当前程序中所有线程的状态
可以使用 thread <thread_id> 命令切换到指定的线程。例如,要调试线程 3,可以输入thread 3 

线程与进程区别

资源
  • 共享资源

线程:线程在同一个进程内运行,所有线程共享进程的全局资源,如地址空间、文件描述符、信号处理程序等。这种共享资源带来了一定的性能优势,但也引入了资源竞争的问题。例如,如果多个线程同时访问共享数据而没有正确的同步机制,就可能会导致数据不一致或竞态条件。

进程:进程有自己独立的资源,包括独立的内存空间、文件描述符等。进程之间没有直接的资源共享,如果需要通信,必须通过进程间通信(IPC)机制(如管道、消息队列、共享内存等)来进行。

  • 私有资源

线程:虽然线程共享大部分资源,但每个线程仍然有自己的私有资源,例如栈(用于函数调用、局部变量等)和寄存器(如程序计数器、栈指针等)。通常,每个线程的栈空间较小,默认大小在 8 MB 左右(这可以在系统配置或程序代码中调整)。

进程:进程的所有资源都是私有的,其他进程无法直接访问。每个进程都有自己的地址空间,典型的用户空间为 3 GB(在 32 位系统上),剩余 1 GB 保留给内核空间。

空间和通信
  • 地址空间

线程:线程共享同一个进程的地址空间,这意味着一个线程可以直接访问另一个线程的数据。这种共享空间允许线程之间进行快速通信和数据交换,但也要求开发者小心管理,以避免数据竞争和死锁等问题。

进程:进程有各自独立的地址空间,这意味着一个进程无法直接访问另一个进程的内存数据。这种隔离增强了系统的稳定性和安全性,但也使得进程间通信变得更为复杂,通常需要使用 IPC 机制来交换数据。

  • 通信

线程:由于线程共享同一个进程的地址空间,所以它们可以通过共享内存进行直接通信。没有额外的开销,可以非常高效地共享数据。

进程:进程之间不能直接共享数据,因此必须依赖操作系统提供的 IPC 机制进行通信。这些机制通常包括管道(pipe)、消息队列(message queue)、共享内存(shared memory)、信号(signal)等。虽然这些方法也可以实现数据交换,但相比线程间的直接通信,效率会稍低一些。

线程的设计框架  posix

创建多线程 >>线程空间操作 >>线程资源回收(栈区)
errno   strerror(errno)  perror();


创建多线程

int pthread_create
(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
  • 功能

该函数可以创建指定的一个线程。

  • 参数

thread 线程id,需要实现定义并由该函数返回。
attr   线程属性,一般是NULL,表示默认属性。
start_routine 指向指针函数的函数指针。
        本质上是一个函数的名称即可。称为th 回调函数,是线程的执行空间。
arg  回调函数的参数,即参数3的指针函数参数。

  • 返回值

成功 0        失败 错误码

注意:一次pthread_create执行只能创建一个线程。
  每个进程至少有一个线程称为主线程。(主线程不需要创建)
  主线程退出则所有创建的子线程都退出。 
  主线程必须有子线程同时运行才算多线程程序。
  线程id是线程的唯一标识,是CPU维护的一组数字。
  pstree 查看系统中多线程的对应关系。
  多个子线程可以执行同一回调函数。

获得当前线程的线程号

pthread_t pthread_self(void); 

返回值的类型unsigned long int; 可用%lu打印

  • 功能

获取当前线程的线程id

  • 参数

  • 返回值

成功 返回当前线程的线程id        失败  -1;

线程的退出

pthread_exit

自行退出>>自杀  >>子线程自己退出
exit(1);

void pthread_exit(void *retval);  exit  return p;
  • 功能

子线程自行退出

  • 参数

retval 线程退出时候的返回状态,临死遗言。

  • 返回值


pthread_cancel

强制退出 >>他杀  >>主线程结束子线程

int pthread_cancel(pthread_t thread);
  • 功能

请求结束一个线程

  • 参数

thread 请求结束一个线程tid

  • 返回值

成功 0        失败 -1

线程的回收


线程的回收机制 

不同与进程没有孤儿线程和僵尸线程
>>主线程结束任意生成的子线程都会结束
>>子线程的结束不会影响主线程的运行


pthread_join

 int pthread_join(pthread_t thread, void **retval);   

 **改变指针的指向

  •   功能

通过该函数可以将指定的线程资源回收,该函数具有阻塞等待功能,如果指定的线程没有结束,回收线程会阻塞。

  •   参数

thread  要回收的子线程tid
retval  要回收的子线程返回值/状态。——ptread_exit(值);

  •   返回值

成功 0        失败 -1;


  子线程的回收策略

1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收。
2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收。
3、如果子线程已知必须长时间运行则,不再回收其资源。


  线程的参数与返回值

#include <pthread.h>
#include <stdio.h>

void *fun(void *arg) {
    int x = *(int *)arg;  // 解引用指针,获取传递的整数值
    printf("Thread received: %d\n", x);
    return NULL;
}

int main() {
    pthread_t tid;
    int x = 10;
    
    pthread_create(&tid, NULL, fun, &x);  // 将 x 的地址传递给线程函数
    pthread_join(tid, NULL);  // 等待线程执行完成
    
    return 0;
}

pthread_create 传递参数

pthread_create(&tid, NULL, fun, &x); 中的 &x 是传递给线程函数 fun 的参数。由于线程函数的参数类型是 void *,所以必须传递指针。

线程函数 fun

void *fun(void *arg) 中的 arg 是一个通用指针,可以指向任何类型的数据

在函数内部,可将 arg 转换为具体的数据类型,这里是 int 类型的指针,然后通过解引用来获取实际的整数值:int x = *(int *)arg;

  • 由于线程函数的参数类型必须是 void *,所以任何参数都需要通过指针传递,甚至是简单的整数。
  • 在线程函数内部,通常需要将 void * 类型的参数转换回实际类型,如将 void *arg 转换为 int *

线程的分离

设置线程分离属性的主要目的是让线程在结束后,系统能够自动回收其资源,而不需要主线程通过 pthread_join 来等待它的结束。对于那些不需要与主线程同步或者不需要获取其返回值的线程,设置分离属性可以简化资源管理。

pthread_detach

int pthread_detach(pthread_t thread);
  • 功能

pthread_detach 函数用于将一个已经创建的线程设置为分离状态。一旦线程被设置为分离状态,当它终止时,其所有资源(如栈、线程控制块等)会自动被系统回收,而不需要调用 pthread_join 来等待它的结束。

  • 参数

thread:线程ID,表示要设置为分离状态的线程。通常传入的是当前线程的ID或其他需要分离的线程的ID。

  • 返回值

0:成功。

非0:失败,返回错误码。

pthread_detach 通常在以下情况下使用:

  • 不需要同步:如果主线程或其他线程不需要等待某个线程的结束(即不需要获取其返回值),可以将该线程设置为分离状态,以便其终止后自动释放资源。
  • 动态设置:可以在线程创建后,动态决定是否将该线程设置为分离状态。

线程的清理

pthread_cleanup_push

void pthread_cleanup_push(void (*routine)(void *), void *arg);
  • 功能

注册一个线程清理函数。当线程被取消或以其他方式退出时,会自动调用这个清理函数,确保资源被正确释放。

  • 参数

routine:清理函数的入口,即一个函数指针。该函数将在清理时被调用。

arg:传递给清理函数的参数。这个参数可以是任何需要在清理时使用的数据或资源句柄。

  • 返回值

无返回值。

pthread_cleanup_pop

void pthread_cleanup_pop(int execute);
  • 功能

调用先前由 pthread_cleanup_push 注册的清理函数。

这个函数通常与 pthread_cleanup_push 成对出现。

  • 参数

execute

如果 execute 非0,则会立即执行清理函数。

如果 execute 为0,则不会执行清理函数。

  • 返回值

无返回值。

标签:函数,int,void,线程,pthread,进程
From: https://blog.csdn.net/weixin_62409078/article/details/141386712

相关文章

  • Java线程
    一、线程的介绍1.1.程序为完成特定任务,用某种语言编写的一组指令的集合。(代码)1.2.进程进程就是指运行中的程序,启动一个进程,操作系统就会为该进程分配内存空间。进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:有它自己的产生、存在和消亡的过程1.3.线程线程......
  • Java线程池详解
    Java线程池解析在Java中有两种方式创建线程池,一种是直接使用Executors工具类创建预先定义好的线程池。一共有以下四种线程池newCachedThreadPool:可缓存的无边界的线程池,最大线程数Integer.MAX_VALUEpublicstaticExecutorServicenewCachedThreadPool(){returnnewTh......
  • 线程安全是什么问题?如何引起?死锁是啥?如何解决?
    目录一、什么是线程不安全?二、如何引起的线程安全?怎么解决?1)CPU调度执行是随机的,抢占式执行(根本原因,硬件层面咱们无法干预)2)多个线程,对同一变量进行修改3)修改操作中,不是“原子”的(重点)死锁是啥,怎么引起的?  (重点)4)内存可见性5)指令重排序  总结-保证线程安全的思路......
  • [Java基础]虚拟线程
    虚拟线程(VirtualThread)是JDK而不是OS实现的轻量级线程(LightweightProcess,LWP),由JVM调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。虚拟线程和平台线程有什么关系?在引入虚拟线程之前,java.lang.Thread包已经支持所谓的平台线程(P......
  • 【C语言】进程和线程详解
    目录C语言进程和线程详解1.进程和线程的对比2.进程的基本概念2.1进程的定义2.2进程的特点2.3进程的生命周期3.进程管理3.1进程创建3.2进程间通信(IPC)3.2.1管道(Pipe)4.线程的基本概念4.1线程的定义4.2线程的特点5.POSIX线程库5.1引用头文件5.2创建线程......
  • 多线程process5
    1、多线程的简介1.1主要概念和三种创建方式主要概念任务、多任务多线程(multithreading)多线程(多条道路),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。普通方法调用......
  • QT中通过Tcp协议的多线程的文件传输(服务器)
    首先新建一个项目命名为SendClientSever因为要进行网络通信,在pro文件的第一行代码中添加network 一、窗口设计拖一个Widget里面放入label,lineEdit,pushbutton,名称如图修改程序设计子线程recvfile类新建一个类用来执行子线程将新建的类的头文件、recvfie.h文件和.cp......
  • 开发多线程程序时,需要注意那些问题
    线程安全竞态条件(RaceCondition):当多个线程同时访问和修改共享资源时,可能会出现竞态条件,导致不确定的行为。需要通过同步机制(如互斥锁、读写锁、原子操作)来保护共享资源。死锁(Deadlock):当两个或多个线程相互等待对方释放锁时,程序会陷入死锁状态。避免死锁的一些策略包括:避......
  • 守护线程和中断线程
    守护线程 java中所有的线程分为两类,一类是守护线程,一类是非守护线程(又称为用户线程)。当java中所有的用户线程结束后,无论此时守护线程的代码是否执行完成,都会被强行中止,整个java程序结束。后台线程publicfinalvoidsetDaemon(booleanon)Daemon代码跟......
  • 在Java中实现通过TCP方式发送和接收Socket消息,包含在多线程状态下的实现
    导言:公司的代码,本来客户端client是前端的unity发送请求的,后面自己写的时候需要在本地进行测试,所以也用Java实现了前端,就写出来记录一下。本文主要展示客户端client跟服务端server基础代码,里面的一些业务逻辑没有进行展示正文1.创建client端首先我们需要创建一个client端进......