【Linux】线程Thread

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

线程概述

线程是进程中的一个执行单元

经典的并发模型,多进程模型,占用大量的内存开销,庞大的进程间调度开销,提供一种开销更小,更轻量级的并发解决方案,多线程技术。

线程在进程中,与进程共享资源和数据,进程是容器,线程是执行单元,进程退出可能导致所有线程退出。

每个进程中都有一个主控线程,还有其他的普通线程,操作系统会给进程分配需要的内存单元。

不支持线程技术的操作系统可以忽略线程概念,进程是调度单位。

多线程操作系统下,进程是内存管理单位,线程才是唯一的调度单元。

线程的CPU分配

Linux操作系统下,线程就是轻量级进程,每个进程分配一个LWP

在Linux操作系统下,所有的调度单位都是进程,淡化线程概念

线程的实现方式

用户线程(User Thread):在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理。可以利用第三方库的形式在不支持线程技术的系统下安装和使用线程,用户级线程的调度开销更小,因为大部分的操作都在用户层完成,无需内核干预

内核线程(Kernel Thread):操作系统中每创建一个线程,系统都会为其创建一个内核对象,此线程系统可以识别支持,会为线程执行资源(时间片)。Control控制器会为每个内核级线程创建内核对象,每个内核级线程都会由CPU进行时间片切换

轻量级进程(LightWeight Process):在内核中来支持用户线程;

进程的蜕化

进程内存资源独占,不与其他人共享,进程是独立的调度单位(无需考虑线程问题)

进程中出现了多个执行单元,讨论考虑线程问题

讨论和分析的蜕化,讨论线程,进程的讨论变为主线程和普通线程的分析和讨论

线程间的共享资源和非共享资源

PCB:共享资源

栈:线程栈空间非共享,每个线程创建后,会分配8MB线程栈

库:库资源共享

堆:共享堆空间

全局资源和静态资源:全局资源共享

代码段:代码段共享

文件描述符表:文件描述符表共享

信号的处理行为(某个线程改变信号行为)对所有线程共享。信号屏蔽字非共享,普通线程拥有独立的屏蔽字,拷贝继承于主线程

TCB:每个线程拥有自己的线程控制块,独立的tid

线程开发相关API接口

NPTL线程库

NPTL线程库是典型的内核级线程库,创建的线程可以被系统标识分配cpu资源(轻量级进程)

native Posix thread library

相关命令

ps aux 进程查看

ps ajx 进程关系查看

ps -eLf 所有线程查看 PID相同的是一组线程

LWPPID 相同的情况表示该进程只有一个线程。

ps -Lf pid 查看某一个进程下的线程

每个线程都会被系统分配lwp 调度编号, 便于系统管理调度线程,但是线程拥有独立的tid,线程id

头文件和函数

#include <pthread.h>使用线程函数的头文件

pthread_t tid线程tid类型

pthread_create

创建线程并指定类型pthread_create(pthread_t *tid,NULL,void * (thread_job)(void *),void *arg)第一个参数是线程id,第二个是线程属性,第三个参数是线程工作地址,第四个参数是线程工作参数。
返回值是err

下面是使用pthread_create创建线程的简单demo程序,通过PIDLWP相同可以看出,这个线程就是主控线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


void * thread_job(void *arg)//普通线程的创建
{
    printf("普通线程被创建,参数为%d\n",*(int*)arg);
    return NULL;
}

int main()
{
    pthread_t tid;
    int code=1024;
    //普通线程都是由主控线程创建的
    pthread_create(&tid,NULL,thread_job,(void*)&code);
        printf("普通线程被主控线程创建出来\n");
    while(1) sleep(1);
    return 0;
}

在编译时需要链接库

gcc Pthrad_Create.c -lpthread -o app

关于线程函数的错误处理

线程函数出错,会返回错误号(err>0),使用char *errstr=strerror(err)进行错误判断

下面是打印错误日志的demo程序:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


void * thread_job(void *arg)//普通线程的创建
{
    //printf("普通线程被创建,参数为%d\n",*(int*)arg);
    while(1) sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    //普通线程都是由主控线程创建的
    while(1)
    {
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
    else
    {
        printf("线程%d成功创建\n",++flags);
    }
    }

    return 0;
}

32位Ubuntu操作系统下,一个进程只能创建381个线程

pthread_self

pthread_tid =pthread_self(void) 成功返回当前线程的id

主线程通过pthread_create创建普通线程,成功传出tid与普通线程自身利用pthread_self()得到的tid值相等但是进程状态不相等,因为pthread_self()获取tid时可以保证当前的有效性,主线程在获取tid的时候,普通线程可能已经退出。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


void * thread_job(void *arg)//普通线程的创建
{
    printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    return NULL;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        while(1) sleep(1);//主控线程还在运行,普通线程已经退出
    return 0;
}

pthread_join(pthread_t tid,void **retval)

线程回收函数,可以回收线程资源的同时获取线程的返回值。经典的阻塞回收函数,会一致等待普通线程结束后,进行回收。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>


void * thread_job(void *arg)//普通线程的创建
{
    printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    return (void*)126;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        pthread_join(tid,&revtal);
        printf("Thread Exit Code:%ld\n",(long int)revtal);
    return 0;
}

可以看到,如果线程工作函数可以正常退出,退出码就是线程工作函数的返回值

pthread_cancel(pthread_t tid)

指定线程tid,取消结束线程

下面是简单的取消一个线程的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

void * thread_job(void *arg)//普通线程的创建
{
    //printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    while(1)
    {
        printf("进程正在工作。。。\n");
        sleep(1);
    }
    return (void*)129;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        sleep(5);
        pthread_cancel(tid);
        pause();
    
    return 0;
}

线程被成功结束,这个进程下只有一个主控线程在工作

当我们对代码稍作修改:

删除线程工作函数中的调用系统函数:

void * thread_job(void *arg)//普通线程的创建
{
    //printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    while(1)
    {
    }
    return (void*)129;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        sleep(5);
        pthread_cancel(tid);
        pause();      
    return 0;
}

结果显示,pthread_cancel并没有成功取消线程。

这有些类似于信号,信号处理的三个切换条件:系统调用,软件中断,软件异常。而pthread_cancel取消事件的处理条件,必须有系统调用。

有一个专门为pthread_cancel提供系统调用的空函数:void pthread_testcancel(void),提供一次空的调用。

在线程工作函数加入这个函数再次尝试:

void * thread_job(void *arg)//普通线程的创建
{
    //printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    while(1)
    {
        pthread_testcancel(void);
    }
    return (void*)129;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        sleep(5);
        pthread_cancel(tid);
        pause();      
    return 0;
}

可以看到只要一个主控线程,普通线程被结束了。

当我们对线程进行取消后,回收一下资源打印退出码看一下:

void * thread_job(void *arg)//普通线程的创建
{
    //printf("普通线程tid %x 创建成功\n",(unsigned int)pthread_self());
    while(1)
    {
        pthread_testcancel(void);
    }
    return (void*)129;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        sleep(5);
        pthread_cancel(tid);
        pthread_join(tid,&revtal);
        printf("Thread Exit Code:%ld\n",(long int)revtal);
        pause();      
    return 0;
}

可以看到退出码是-1,所以线程指定退出码时不允许使用-1,保留给pthread_cancel

pthread_detach(pthread_t tid)

用于将一个线程设置为分离态线程。

可以通过线程属性,批量创建分离态线程,这些线程诞生即是分离态。

线程有回收态和分离态,这两种状态是互斥的。

若修改线程退出状态,从回收态改为分离态,此操作不可逆,不能将分离线程变为回收态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <pthread.h>

void * thread_job(void *arg)//普通线程的创建
{
    pthread_detach(pthread_self());
    while(1)
    {
        pthread_testcancel();
    }
    return (void*)129;
}

int main()
{
    pthread_t tid;
    int code=1024;
    int flags=0;
    int err;
    void *revtal=NULL;
    //普通线程都是由主控线程创建的
    if((err=pthread_create(&tid,NULL,thread_job,(void*)&code))>0)
    {
        printf("thread create failed:%s\n",strerror(err));
        exit(0);//创建失败,进程退出
    }
        printf("Monster Thread Tid:%x 普通线程Tid:%lx \n",(unsigned int)pthread_self(),tid);
        if((err=pthread_join(tid,&revtal))>0)
        {
            printf("join call failed %s\n",strerror(err));
        }
        printf("Thread Exit Code:%ld\n",(long int)revtal);
        pause();
        return 0;
}

joindetach会竞争时间片,所以有时线程以detach分离态被创建,当线程处于分离态时,无法手动回收,join执行失败

如果对一个分离态线程进行回收操作,pthread_join会失效返回。对一个已经处于回收阶段(join已经开始阻塞等待了)的线程设置分离,分离设置失败。

一个自行回收,一个由系统回收,一个可以得到线程退出码,一个无法获取

pthread_exit(void * exitcode)

线程退出函数,结束当前线程,与进程无关,退出码可以被join获取

线程的退出方式

return 0

主线程结束进程;结束普通线程,返回结果

pthread_cancel

主线程结束,与进程无关

普通进程结束,与进程无关

使用普通线程对主线程进行回收,产生了僵尸级线程

pthread_exit()

主线程结束,与进程无关

普通进程结束,与进程无关

exit()

进程退出,释放所有线程

线程属性

在使用创建线程函数时指定线程类型pthread_create(pthread_t *tid,pthread_attr_t *attr,void * (thread_job)(void *),void *arg)

用第二个参数指定线程属性

pthread_attr_t线程属性类型,是一个结构体类型

修改线程程属性的操作

使用pthread_attr_getdetachstate查看线程默认退出属性

使用pthread_attr_getstack查看栈属性


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>


int main()
{
    int State;
    void *stack_addr;//栈空间地址
    size_t stacksize;//栈空间默认大小
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_getdetachstate(&attr,&State);
        if(State==PTHREAD_CREATE_JOINABLE)
        printf("Thread Default State is Join\n");
    else
        printf("Thread Default State is Detach\n");
    pthread_attr_getstack(&attr,&stack_addr,&stacksize);
    printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);
    return 0;
}

使用pthread_attr_setdetachstate修改线程默认退出状态为分离态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>

void *thread_job(void * arg)
{

}

int main()
{
    int State;
    void *stack_addr;//栈空间地址
    size_t stacksize;//栈空间默认大小
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_getdetachstate(&attr,&State);
        if(State==PTHREAD_CREATE_JOINABLE)
        printf("Thread Default State is Join\n");
    else
        printf("Thread Default State is Detach\n");
    pthread_attr_getstack(&attr,&stack_addr,&stacksize);
    printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);

    pthread_t tid;

    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    pthread_create(&tid,&attr,thread_job,NULL);
    int err=pthread_join(tid,NULL);
    if(err>0)
        printf("Join Call Failed:%s\n",strerror(err));
    return 0;
}

使用pthread_attr_setstacksize修改默认线程栈大小

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <pthread.h>

void *thread_job(void * arg)
{

}

int main()
{
    int State;
    void *stack_addr;//栈空间地址
    size_t stacksize;//栈空间默认大小
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_getdetachstate(&attr,&State);
        if(State==PTHREAD_CREATE_JOINABLE)
        printf("Thread Default State is Join\n");
    else
        printf("Thread Default State is Detach\n");
    pthread_attr_getstack(&attr,&stack_addr,&stacksize);
    printf("线程栈地址为%p 栈大小为%d\n",stack_addr,stacksize);

    pthread_t tid;

    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    stacksize=0x100000;
    int err;
    int flags=0;
    while(1)
    {
        if((stack_addr=(void*)malloc(stacksize))==NULL)
        {
            printf("malloc Stack Failed\n");
        }
        pthread_attr_setstacksize(&attr,stacksize);
        if((err=pthread_create(&tid,&attr,thread_job,NULL))>0)
        {
            printf("Create failed:%s\n",strerror(err));
            exit(0);
        }
        printf("Thread %d 创建成功\n",++flags);
    }
    return 0;
}

由结果可知,当将默认大小变小后,32位系统下可以创建的线程数量增加了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

期货交易记录20240626

文章目录 期货交易系统构建第一步、选品第二步、心态历练第三步、开仓纪律第四步、持仓纪律第五步、接下来的计划 2024年6月26号&#xff0c;开始写期货交易的第四篇日记。 交易记录&#xff1a;做了一笔纯碱的多单&#xff0c;在回撤了400个点左右后&#xff0c;看到企稳信号后…

标签接口开发(富含完整CRUD开发流程)

文章目录 1.easyCode生成CRUD1.生成代码2.查看代码3.调整代码1.SubjectLabelDao.xml发现生成的select语句不带逗号&#xff01;&#xff01;&#xff01;1.解决方法&#xff1a;2.entity.java.vm3.dao.java.vm4.Mapper.xml.vm 2.重新生成代码3.SubjectLabelDao.java 删除Pageab…

ArkTS开发系列之Web组件的学习(2.9)

上篇回顾&#xff1a;ArkTS开发系列之事件&#xff08;2.8.2手势事件&#xff09; 本篇内容&#xff1a; ArkTS开发系列之Web组件的学习&#xff08;2.9&#xff09; 一、知识储备 Web组件就是用来展示网页的一个组件。具有页面加载、页面交互以及页面调试功能 1. 加载网络…

【Java】Java序列化和反序列化

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 # Java中的序列化和反序列化 在Java中&#xff0c;序列化是将对象的状态写入字节流的机制。它主要用于Hibernate…

国家自然科学基金标书大全(2002-2024)

数据来源&#xff1a;在20世纪80年代初&#xff0c;为了促进中国的科技体制革新并改革科研资金分配机制&#xff0c;中国科学院的89位院士联名向党和国家领导人提出建议&#xff0c;设立了国家自然科学基金的设立。国自然基金自创立以来&#xff0c;根据国家发展科学技术方针、…

可以一键生成热点营销视频的工具,建议收藏

在当今的商业环境中&#xff0c;热点营销已经成为了一种非常重要的营销策略。那么&#xff0c;什么是热点营销呢&#xff1f;又怎么做热点营销视频呢&#xff1f; 最近高考成绩慢慢公布了&#xff0c;领导让结合“高考成绩公布”这个热点&#xff0c;做一个关于企业或产品的营销…

力扣:59. 螺旋矩阵 II(Java,模拟)

目录 题目描述示例 1&#xff1a;代码实现 题目描述 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5…

想布局短视频赛道,云微客AI矩阵系统告诉你诀窍

随着人工智能技术的不断发展&#xff0c;越来越多的企业和个人创作者开始意识到智能化的重要性。而现阶段&#xff0c;随着短视频市场的膨胀扩大&#xff0c;批量成片、智能创作、定时发布是当下重要的趋势&#xff0c;企业如果想在短视频赛道分一杯羹&#xff0c;智能化的平台…

七天速通javaSE:第二天 基础:标识符与数据类型

文章目录 前言一、注释与标识符1. 注释2. 标识符2.1 标识符2.2 关键字 二、数据类型1. 语言类型2. 数据类型2.1 基本数据类型2.2引用数据类型 三、类型转换1. 自动转换2. 强制转换&#xff08;不建议&#xff09; 四、代码规范 前言 今天将学习Java语法的基础&#xff0c;认识…

自然语言处理——英文文本预处理

高质量数据的重要性 数据的质量直接影响模型的性能和准确性。高质量的数据可以显著提升模型的学习效果&#xff0c;帮助模型更准确地识别模式、进行预测和决策。具体原因包括以下几点&#xff1a; 噪音减少&#xff1a;高质量的数据经过清理&#xff0c;减少了无关或错误信息…

open()函数——打开文件并返回文件对象

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 open()函数用于打开文件&#xff0c;返回一个文件读写对象&#xff0c;然后可以对文件进行相应读写操作。 语法参考 open()函数的语法格式如下&…

Vite响应Ajax请求

Vite响应Ajax请求 陈拓 2024/06/20-2024/06/24 1. 概述 http-server、live-server 等常用于本地测试和开发的http服务器不能很好的支持 ES 模块&#xff0c;在测试ES 模块时浏览器控制台经常显示错误&#xff1a; Failed to load module script: Expected a JavaScript modu…

【TOOL】ceres学习笔记(二) —— 自定义函数练习

文章目录 一、曲线方程1. 问题描述2. 实现方案 一、曲线方程 1. 问题描述 现有数学模型为 f ( x ) A e x B s i n ( x ) C x D f(x)Ae^xBsin(x)Cx^D f(x)AexBsin(x)CxD &#xff0c;但不知道 A A A 、 B B B 、 C C C 、 D D D 各参数系数&#xff0c;实验数据中含有噪声…

llm-universe | 四. 构建RAG应用

构建RAG应用 一.将LLM 接入 LangChain二.构建检索问答链1.加载向量数据库2.创建一个 LLM3.构建检索问答链4.检索问答链效果测试5.添加历史对话的记忆功能5.1 记忆&#xff08;Memory&#xff09;5.2 对话检索链&#xff08;ConversationalRetrievalChain&#xff09; 三. 部署知…

11-Django项目--Ajax请求二

目录 模版: demo_list.html perform_list.html 数据库操作: 路由: 视图函数: Ajax_data.py perform.py 模版: demo_list.html {% extends "index/index.html" %} {% load static %} # 未实现修改,删除操作{% block content %}<div class"container…

基于YOLOv8的多端车流检测系统(用于毕设+开源)

目录 ✨基于YOLOv8&#x1f680;的多端车流检测系统-MTAS (Multi-Platform Traffic Analysis System) 一、基本功能介绍 1、客户端 &#xff08;pyside6yolov8pytorch&#xff09; 2、网页端&#xff08;Vue3TypestriptPython3MySQL&#xff09; 3、创新点&#xff08;毕设需…

2024年最新通信安全员考试题库

61.架设架空光缆&#xff0c;可使用吊板作业的情况是&#xff08;&#xff09;。 A.在2.2/7规格的电杆与墙壁之间的吊线上&#xff0c;吊线高度5m B.在2.2/7规格的墙壁与墙壁之间的吊线上&#xff0c;吊线高度6m C.在2.2/7规格的电杆与电杆之间的吊线上&#xff0c;吊线高度…

【嵌入式 RT-Thread】一种优雅的使用 [互斥锁] 和 [信号量] 解决数据多路并发思路

rt-thread 中的信号量和互斥锁在工业开发项目中的应用&#xff0c;本博文主要介绍了一种优雅的使用 [互斥锁] 和 [信号量] 解决数据多路并发思路 2024-06 by 积跬步、至千里 目录 0. 个人简介 && 授权须知1. 工业场景描述1.1 工业数据采集需求1.2 总线协议与数据采集 2…

杭州代理记账报税全程托管专业实力全面指南

杭州代理记税报税服务可以为企业提供全程托管财务管理解决方案&#xff0c;确保企业的财务工作专业、高效、合规。以下是杭州代理记税报税服务全面指南&#xff1a; https://www.9733.cn/news/detail/185.html 一、代理记账报税服务的内容 基础服务&#xff1a; 每日记&#xf…

昇思25天学习打卡营第3天|张量Tensor

认识张量 张量&#xff0c;是一个数据结构&#xff0c;也可以说是一个函数&#xff0c;它描述了标量、矢量和张量之间线性关系。这些关系包括 内积、外积、线性映射以及笛卡尔积。张量中既有大小、又有方向。张量由多个数值构成&#xff0c;在n维空间里&#xff0c;会出现 n …