Linux高级编程——线程

pthread 线程
   

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

  优点: 比多进程节省资源,可以共享变量

进程会占用3g左右的空间,线程只会占用一部分,大概8M的空间

进程的父子不会共享,但一个进程之间的线程的资源可以共享.

进程的父子不是平级关系,线程是平级关系

 特征:s's
    1、共享资源
    2、效率高  30%
    3、三方库: pthread  clone   posix
            3.1 编写代码头文件: pthread.h
            3.2 编译代码加载库: -lpthread   library 
            libpthread.so  (linux库)
            gcc 1.c -lpthread     -lc
    缺点:
    1,线程和进程相比,稳定性,稍微差些
    2,线程的调试gdb,相对麻烦些。
        info thread 
        *1  
        2 
        3
        thread 3 
        
线程与进程区别:

    资源:
        线程比进程多了共享资源。  IPC
        线程又具有部分私有资源。
        进程间只有私有资源没有共享资源。
    空间:
        进程空间独立,不能直接通信。
        线程可以共享空间,可以直接通信。

       进程解决相对复杂的问题,线 程解决相对复杂的问题.

共同点:

二者都可以并发

3、线程的设计框架  posix
    

创建多线程 ==》线程空间操作 ===》线程资源回收
errno   strerror(errno)  perror();
  

 3.1 创建多线程:


    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执行只能创建一个线程。
      每个进程至少有一个线程称为主线程。
      主线程退出则所有创建的子线程都退出。暂时先用while(1); 
      主线程必须有子线程同时运行才算多线程程序。
      线程id是线程的唯一标识,是CPU维护的一组数字。
      pstree 查看系统中多线程的对应关系。
      多个子线程可以执行同一回调函数。
    ps -eLf 查看线程相关信息Low Weigth Process
    ps -eLo pid,ppid,lwp,stat,comm

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *th1(void*arg)
{while(1){printf("发送视频\n");sleep(1);}
}void *th2(void*arg)
{while(1){printf("接受控制\n");}
}int main(int argc, const char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1);return 0;
}
  1. main 函数开始执行。
  2. 使用 pthread_create 创建了两个线程 tid1 和 tid2
  3. th1 线程开始执行其无限循环,并在每次迭代中打印 "发送视频",然后暂停一秒。
  4. 同时(几乎是同时),th2 线程也开始执行其无限循环,不断打印 "接受控制"。
  5. 因为两个线程是并发执行的,所以它们之间没有固定的打印顺序。这取决于操作系统调度器的决策,哪个线程在何时获得CPU时间片。
  6. main 函数中的 while(1); 是一个空循环,它使主线程保持活动状态,防止程序立即退出。然而,这个空循环并没有为程序提供任何有用的功能,通常你可能会使用某种形式的线程同步或等待(如 pthread_join)来确保主线程在所有其他线程完成后才退出。

此时输出是乱的,是由于

  • 线程调度是由操作系统控制的,它决定哪个线程在何时运行。这取决于许多因素,包括线程优先级、系统负载、可用的CPU核心数量等。
  • 由于两个线程都在无限循环中,并且没有同步机制(如互斥锁、条件变量等),所以它们会尽可能快地交替执行(或并行执行,如果系统有多个CPU核心),导致输出看起来没有规律。

2、pthread_t pthread_self(void); unsigned long int; %lu  获取线程号


   功能:获取当前线程的线程id
   参数:无
   返回值:成功 返回当前线程的线程id
               失败  -1;
            syscall(SYS_gettid);
这个方法重启后失效
alias gcc='gcc -g -pthread '
unalias gcc 

永久起作用
cd ~ //家目录
vim .bashrc
alias gcc='gcc -g -pthread '  :wq

source .bashrc  生效

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("发送视频 %lu\n",pthread_self());sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制 %lu\n",pthread_self());sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}
  • 使用pthread_create创建两个线程:tid1(运行th1)和tid2(运行th2)。
  • 打印主线程的ID。
  • 使用while(1);使主线程进入无限循环,以保持程序运行。否则,当主线程结束时,程序可能会立即终止,导致其他线程也被终止

练习题:
    设计一个多线程程序,至少有三个子线程
    每个线程执行不同的任务,并实时打印执行
    过程,同时表明身份。

    eg: ./a.out  ==>tid =xxx...  zheng ...
                    tid2 = xxx wozai.
                    tid3 = xxx  wozai ssss


线程的退出:

1.直接用return;   

2: 自行退出 ==》自杀  ==》子线程自己退出
        exit(1);
        void pthread_exit(void *retval);  exit  return p;
        功能:子线程自行退出
        参数: retval 线程退出时候的返回状态,临死遗言。
        返回值:无

            th
            {
                int a =10;

                pthread_exit(&a);
            }
            join(,&ret)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i =10;while(i--){printf("发送视频 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);//return NULL;
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}


    3. 强制退出 ==》他杀  ==》主线程结束子线程
        int pthread_cancel(pthread_t thread);
        功能:请求结束一个线程  (在主线程种调用 写入某个线程id号,可以关闭该线程)
        参数:thread 请求结束一个线程tid(想要关闭的线程id号)
        返回值:成功 0
                失败 -1;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("发送视频\n");sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);int i = 0 ;while(1){i++;if(3 == i ){pthread_cancel(tid1);}if(5 ==i){pthread_cancel(tid2);}sleep(1);}return 0;
}

作业:
    创建一个多线程程序,至少有10个子线程,
    每个线程有会打印不同的数据,同时表明身份。

    
    线程的回收


    1、线程的回收机制 ====》不同与进程没有孤儿线程和僵尸线程。
                                    ====》主线程结束任意生成的子线程都会结束。
                                      ====》 子线程的结束不会影响主线程的运行。
    char * retval ; retval++; 1 
    int * retval; 

    int pthread_join(pthread_t thread, void **retval);    
  功能:通过该函数可以将指定的线程资源回收,该函数具有阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。
  参数:thread  要回收的子线程tid
              retval  要回收的子线程返回值/状态。==》ptread_exit(值);
  返回值:成功 0
                 失败 返回一个错误号,是一个大于零的数;

                  失败可以用

               

 

#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i = 10;while(i--){printf("发送视频\n");sleep(1);}
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);int ret = pthread_create(&tid2,NULL,th2,NULL);if(ret!=0){// perror()fprintf(stderr,"error %s\n",strerror(ret));//exit();}pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0;
}

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


  线程的参数,返回值
  

1、传参数
        
    传整数 ===》int add(int a,int b);  ///a b 形参
                add(x,y);    x y 实参 

        pthread_create(&tid,NULL,fun,x);

        fun ==>void * fun(void * arg);
        
    练习:创建一个子线程并向该线程中传入一个字符在
          线程中打印输出。
          在此基础上向子线程中传入一个字符串,并在
          子线程中打印输出。

          
          add(int a, int b)
          {
            int c = a+b;
            char buf[]=""
            return c;
          }
                    5
          int d = add(2,3);

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static int a =20;return &a;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %d\n",*(int*)ret);return 0;
}


    传字符串
        栈区字符数组:
        字符串常量:
        char *p = "hello";
        堆区字符串;
            char *pc = (char *)malloc(128);
            ptread_create(&tid,NULL,fun,pc);

            pthread_join(tid,NULL);

            free(pc);
        
            fun(void *arg)
            {
                char * pc = (char *)arg    ;
                printf("%s \n",pc);
                        %c
            }

栈区

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static char buf[256]={0};strcpy(buf,"要消亡了\n");return buf;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);return 0;
}

堆区:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{char * tmp = (char* )arg;strcpy(tmp,"hello");return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;char * p = (char*)malloc(50);void* ret;pthread_create(&tid,NULL,th,p);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);free(p);return 0;
}

  

 传结构体
    1、定义结构体类型
    2、用结构体定义变量
    3、向pthread_create传结构体变量
    4、从fun子线程中获取结构体数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
typedef struct 
{char * p;int a;
}TH_ARG;
void* th(void* arg)
{TH_ARG * tmp = (TH_ARG* )arg;strcpy(tmp->p,"hello");//strcpy( ((TH_ARG*)arg)->p ,"hello");tmp->a +=10;return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;int a  =20;char * p = (char*)malloc(50);TH_ARG arg;arg.a = a;arg.p = p;void* ret;pthread_create(&tid,NULL,th,&arg);pthread_join(tid,&ret);printf("ret %s %d\n",((TH_ARG*)ret)->p,((TH_ARG*)ret)->a);free(p);return 0;
}

练习:
    定义一个包含不同数据类型的测试结构体
    并向子线程传参数,同时在子线程中打印输出。


    定义一个回调函数可以完成计算器的功能
    定义一个数据结构体可以一次传入不同的数据
    和计算方式并将结果打印输出。
    //2 + 3.6 
    // 2 + 3  2+3
    // 8 * 6
    typedef strcut
    {
        float a;
        float b;
        char c;//+ - * / 
        float d;
    }JSQ;
    
    

返回值:pthread_exit(0) ===>pthread_exit(9);
        pthread_join(tid,NULL); ===>pthread_join(tid,?);
10;
-10;
int * p =malloc(4);
*p = -10;
1、pthread_exit(?) ==>? = void * retval;
                          纯地址

2、pthread_join(tid,?) ==>? = void **retval;
                            地址的地址
原理:子线程退出的时候,可以返回一个内存地址
      改值所在的内存中可以存储任何数据,只要
      地址存在,则数据都可以正常返回。
    
    地址有三种:
    0、栈区变量  错误,子线程结束该地址失效。
    1、全局变量  失去意义,本质可以直接访问。

    2、静态变量 
    3、堆区变量


      主线程通过一个地址形式的变量来接受子进程
      返回的地址变量就可以将该地址中的数据取到。

    练习:从子线程中申请一块堆区内存并存字符串
      将该字符串以返回值形式返回到主线程并打印输出。
          
 

   设置分离属性,目的线程消亡,自动回收空间。
  

主线程没有空,才设置分离属性来回收.

 attribute

 int pthread_attr_init(pthread_attr_t *attr);
    功能,初始化一个attr的变量
    参数:attr,需要变量来接受初始值
    返回:0  成功,
    非0 错误;
       int pthread_attr_destroy(pthread_attr_t *attr);
      功能:销毁attr变量。
      attr,属性变量
      返回:0  成功,
    非0 错误;
       
       
    man -k 
     int pthread_attr_setdetachstate(pthread_attr_t *attr
, int detachstate);

    功能:把一个线程设置成相应的属性
    参数,attr,属性变量,有init函数初始化他。
    detachstate:有2个可选值,
    
    PTHREAD_CREATE_DETACHED:设置分离属性。
    
    第二种设置分离属性:
int pthread_deatch(pthread_t thread);
    功能,设置分离属性
    参数,线程id号,填自己的id
    
    do{
    
    
    }while()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* th(void* arg)
{pthread_detach(pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;int i = 0 ;for(i=0;i<50000;i++){int ret = pthread_create(&tid,NULL,th,NULL);if(ret!=0){break;}// pthread_detach(tid);}printf("%d \n",i);return 0;
}


void pthread_cleanup_push(void (*routine)(void *), void *arg);

    功能:注册一个线程清理函数
    参数,routine,线程清理函数的入口
        arg,清理函数的参数。
    返回值,无
        
void pthread_cleanup_pop(int execute);
    功能:调用清理函数
    execute,非0  执行清理函数
            0 ,不执行清理
            
    返回值,无

do
{

}while(1)

process                thread
fork                pthread_create 
getpid,ppid,        pthread_self
exit,                pthread_exit 
wait,waitpid,        pthread_join 
kill,                pthread_cancel
atexit                 pthread_clean,
exec                system--->fork->exec (ls)
                    
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/363357.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

市场拓展招聘:完整指南

扩大招聘业务会给你带来很多挑战&#xff0c;更不用说你已经在处理的问题了。助教专业人士每周花近13个小时为一个角色寻找候选人。此外&#xff0c;客户的需求也在不断变化&#xff0c;招聘机构之间的竞争也在加剧。毫无疑问&#xff0c;对增长有战略的方法会有很大的帮助。一…

RocketMQ快速入门:事务消息原理及实现(十)

目录 0. 引言1. 原理2. 事务消息的实现2.1 java client实现&#xff08;适用于spring框架&#xff09;2.2 springboot实现 3. 总结 0. 引言 rocketmq 的一大特性就是支持事务性消息&#xff0c;这在诸多场景中有所应用。在之前的文章中我们已经讲解过事务消息的使用&#xff0…

无需向量量化的自回归图像生成

摘要 https://arxiv.org/pdf/2406.11838 传统观点认为&#xff0c;用于图像生成的自回归模型通常伴随着向量量化的标记。我们观察到&#xff0c;尽管离散值空间可以方便地表示分类分布&#xff0c;但它对于自回归建模来说并不是必需的。在这项工作中&#xff0c;我们提出使用扩…

【2024大语言模型必知】做RAG时为什么要使用滑动窗口?句子窗口检索(Sentence Window Retrieval)是什么?

目录 1. 传统的向量检索方法&#xff0c;使用整个文档检索&#xff0c;为什么不行&#xff1f; 2.句子滑动窗口检索&#xff08;Sentence Window Retrieval&#xff09;工作原理 3.句子滑动窗口检索&#xff08;Sentence Window Retrieval&#xff09;的优点 1. 传统的向量检…

Java的IO体系

目录 1、Java的IO体系2、IO的常用方法3、Java中为什么要分为字节流和字符流4、File和RandomAccessFile5、Java对象的序列化和反序列化6、缓冲流7、Java 的IO流中涉及哪些设计模式 1、Java的IO体系 IO 即为 input 输入 和 output输出 Java的IO体系主要分为字节流和字符流两大类…

nginx添加模块

问题描述&#xff1a;已经在运行的宝塔中的nginx如何添加模块 1. 进入宝塔nginx的脚本目录 cd /www/server/panel/install 2. 读修改宝塔官方写的脚本 vim nginx.sh 3. 找到字符 ./configure - 添加模块 --add-module/home/root/app/nginx-module/echo-nginx-module-0.62 …

AI专区上新啦!豆包、通义、360AI、天工AI、澜舟智库等入驻麒麟软件商店

继百度文心一言、讯飞星火、博思白板、雅意等AI产品上架后&#xff0c;麒麟软件商店再添新成员&#xff01;近日&#xff0c;豆包、通义、360AI搜索、360智脑、360智绘、昆仑万维天工AI、澜舟智库等重磅AI产品登陆麒麟软件商店人工智能专区&#xff0c;涵盖了AI对话、AI写作、A…

零知识证明基础:数字签名

1、绪论 数字签名(Digital Signature)&#xff0c;也称电子签名&#xff0c;是指附加在某一电子文档中的一组特定的符号或代码。它利用密码技术对该电子文档进行关信息提取并进行认证形成&#xff0c;用于标识签发者的身份以及签发者对电子文档的认可&#xff0c;并能被接收者…

【shell脚本速成】python安装脚本

文章目录 案例需求应用场景解决问题脚本思路案例代码 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f388;欢迎踏入我的博客世界&#xff0c;能与您在此邂逅&#xff0c;真是缘分使然&#xff01;&#x1f60a; &#x1f338;愿您在此停留的每一刻&#xff0c;都沐…

双路视频同屏显示(拼接)-基于野火Zynq7020开发板

前情提要 米联客FDMA驱动OV5640摄像头—基于野火Zynq7020开发板 本文在此基础上&#xff0c;实现了双路视频拼接。将ov5640输出的1024600的图像数据缩放为512600&#xff0c;分两路写入ddr3&#xff0c;并且显示在1024*600的RGB屏幕中。 纯FPGA也可以按此方法实现。 总体BLOC…

毕业答辩制作PPT【攻略】

毕业答辩制作PPT【攻略】 前言版权毕业答辩制作PPT【攻略】一、WPS AI 15天免费会员二、AI文档生成PPT三、修改完善PPT 最后 前言 2024-06-14 23:43:05 以下内容源自《【攻略】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN…

设计模式——状态模式

状态模式 状态模式是一种行为设计模式&#xff0c;让你能在一个对象的内部状态变化时改变其行为&#xff0c;使其看上去就像是改变了自身所属的类一样 其主要思想是程序在任意时刻仅可处于几种有限的状态中。 在任何一个特定状态中&#xff0c; 程序的行为都不相同&#xff0…

arco disign vue 日期组件的样式穿透

问题描述: 对日期组件进行样式穿透. 原因分析: 如图,日期组件被展开时它默认将dom元素挂载到body下, 我们的页面在idroot的div 里层, 里层想要穿透外层是万万行不通的. 解决问题: 其实官网提供了参数,但是并没有提供例子, 只能自己摸索着过河. 对于日期组件穿透样式,我们能…

ai智能语音机器人在电销里发挥怎样的作用

得益于语音识别技术的的进步&#xff0c;人工智能发展越来越成熟。相信作为企业的管理者&#xff0c;都遇到过这样的事&#xff1a;一个电销新人刚刚入行&#xff0c;需求经过一两个月的学习培训才能成为一名合格的销售人员。在这段学习的期间&#xff0c;企业投入的成本是没有…

WebSocket走私实践(附赠LiveGBS监控系统未授权管理员密码重置)

WebSocket走私实践&#xff08;附赠LiveGBS监控系统未授权管理员密码重置&#xff09; 对此&#xff0c;我特别感谢TryHackMe和HackTheBox academy&#xff0c;永远相信和追随英国TryHackMe所教导的网络安全知识,并保持学习 WebSocket走私相关的知识在这里 前段时间学习过htt…

数字信号处理实验二(模拟信号采样与重构及频谱分析FFT)

模拟信号采样与重构及频谱分析FFT&#xff08;2学时&#xff09; 要求&#xff1a; 对一模拟信号进行采样&#xff1b;对该采样信号进行重构&#xff1b;分析它们的频谱特征。目的&#xff1a; 熟悉MATLAB命令和编辑、运行、调试环境&#xff1b;掌握采样定理及对信号的频谱分析…

vue3中获取Excel和csv文件中的内容

1.效果 2.安装 npm install xlsxyarn add xlsx 3.引入使用 <el-upload ref"uploadRef" :on-change"changeFile" :show-file-list"false" class"mr10" accept".csv, .xlsx, .xls"action"#" :auto-upload&quo…

[保姆级教程]uniapp自定义标签页切换组件

文章目录 导文样式改成动态列表切换点击效果加上点击自动滑动scroll-view加上切换组件效果 导文 unaipp自带的标签页和ui设计相差太大&#xff0c;直接修改组件比手写一个还麻烦&#xff0c;下面手写一个。 样式 先用scroll-view做一个滑动&#xff0c;不然多的话滑动不了。 &l…

Vue3 国际化i18n

国际化i18n方案 1. 什么是i18n2. i18n安装、配置及使用2.1 安装2.2 配置2.3 挂载到实例2.4 组件中使用2.5 语言切换 1. 什么是i18n i18n 是“国际化”的简称。在资讯领域&#xff0c;国际化(i18n)指让产品&#xff08;出版物&#xff0c;软件&#xff0c;硬件等&#xff09;无…

数据库调优厂商 OtterTune 宣布停止运营

昨天刷到消息&#xff0c;得知数据库优化厂商 OtterTune 停止了运营。OtterTune 的成员主要来自 CMU Andy Pavlo 教授领导的数据库实验室。公司正式成立于 2021 年 5 月&#xff0c;融资了 1450 万美金。 按照 Andy 教授的说法&#xff0c;公司是被一个收购 offer 搞砸了。同时…