十五、多线程(上)

news2024/9/29 22:45:02

文章目录

  • 一、线程
    • (一)什么是线程
    • (二)Linux下的多线程
    • (三)总结
    • (四)线程优点
    • (五)线程缺点
    • (六)线程异常
    • (七)线程用途
  • 二、Linux下的进程VS线程
    • (一)进程和线程
    • (二)进程的多个线程共享
    • (三)进程和线程的关系
  • 三、页表理解——虚拟到物理地址之间的转化
    • (一)页表理解
    • (二)页表的好处
      • 1.进程虚拟地址管理和内存管理,通过页表+page进行解耦
      • 2.节省空间:分页机制+按需创建页表
  • 四、Linux线程控制
    • (一)POSXI线程库
      • 1.错误检查:
      • 2.线程创建:pthread_create
      • 3.获取进程ID:pthread_self
      • 4.进程等待:pthread_join
      • 5.进程终止:pthread_exit
      • 6.pthread_cancel:进程取消
      • 7.线程概念代码演示:
  • 五、用户级线程概念
    • (一)线程异常了怎么办?
    • (二)理解 pthread_ t
    • (三)线程栈
      • 1.代码区有三类代码:
      • 2.解释 pthread_create 创建线程的返回值pthread_t
      • 3.线程局部存储
  • 六、线程分离
    • (一)概念
    • (二) 例子
      • 1.pthread_ detach(pthread_ self()); 新线程自我分离
      • 2.pthread_ detach(tid1);主线程分离新线程
    • (三) 线程分离可以理解为线程退出的第四种方式

一、线程

(一)什么是线程

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是"一个进程内部的控制序列"。
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

一般在Linux教材里面线程是这样定义的:
线程:是在进程内部运行的一个执行分支(执行流),属于进程的一部分,粒度要比进程更加细和轻量化。

按照上面教材的这种说法,现在有一些问题:

一个进程内可能存在多个线程?

这是可能呢。

进程:线程 = 1 : N

在OS中存在这么多的线程,那么OS要不要管理线程呢?如何管理呢?

答案是肯定要管理的,六字真言:先描述,再组织。

因此我们就可以得出一个推论:线程也应该要有线程控制块TCB

的确,我们window下的多线程就是这样子做的。

(二)Linux下的多线程

但是我们Linux下的多线程并没有像上面说的那样通过创建数据结构,然后通过管理数据结构从而达到管理线程的目的。

那我们Linux下的多线程是怎么做的呢?

我们先来回顾一下进程,我们知道创建一个进程,我们需要为它创建一些列的数据结构,比如说:PCB(进程控制块)、mm_struct(进程地址空间)、页表和file_struct等等。

在这里插入图片描述

CPU此时看到的PCB是<=我们之前讲的PCB的概念的,在CPU看来一个PCB就是一个需要被调度的执行流。
这就是我们Linux下的线程,Linux中并没有像windows下为线程专门设计TCP,Linux的线程是用进程模拟的,而是使用进程PCB来模拟线程。

那Linux这样做有什么好处呢?

不用维护复杂的进程和线程的关系,不用单独为线程涉及任何算法,直接使用进程的一套相关的方法,OS只需要聚焦在线程间的资源分配上就可以了。

现在我们再来理解一下上面教材说的:线程是在进程内部运行的一个执行分支,这里的内部是什么意思呢?,那什么又叫做一个执行分支呢?

这里的内部指的是线程是在进程的虚拟地址空间中运行的。
执行分支指的是CPU调度的时候只看PCB,每一个PCB曾经被指派过指向方法和数据,CPU是可以直接调度的。

在今天了解了Linux下的线程之后,我们又该如何理解我们之前讲的进程呢?

下面用绿色方框圈起来的内容,我们将这个整体叫做进程。

(三)总结

  • 之前的进程,内部只有一个执行流,今天的进程,内部可以具有多个执行流。
  • 创建进程的"成本非常高",成本:时间+空间,创建一个进程要使用的资源是非常多的(0-1),我们从内核视角来看进程就是承担分配系统资源的基本实体!!
  • 线程就是CPU调度的基本单位,承担进程资源的一部分的基本实体,进程划分资源给线程。
  • 总得来说 Linux下的线程就是轻量级进程。
    曾经:进程——内核数据结构+进程对应的代码和数据
    现在:进程——内核视角:承担分配系统资源的基本实体 (进程的基座属性),即:向系统申请资源的基本单位!

内部只有一个执行流 task_struct 的进程——单执行流进程
内部有多个执行流 task_struct 的进程——多执行流进程
线程(执行流)是调度的基本单位!

下面紫色框起来的进程PCB,虚拟内存,页表,内存中的数据和代码,这一组资源的集合叫做一个进程。
在这里插入图片描述

(四)线程优点

  • 创建一个新线程的代价要比创建一个新进程要小得多。
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
    线程占用的资源要比进程少很多。
  • 能充分利用多处理器的可并行数量。
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
  • 使用多线程可以更加充分利用cpu资源,使任务处理效率更高,进而提高程序响应,即:耗时的操作使用线程,提高应用程序响应
  • 对于多核心cpu来说,每个核心都有一套独立的寄存器用于进行程序处理,因此可以同时将多个执行流的信息加载到不同核心上并行运行,充分利用cpu资源提高处理效率,即:多CPU系统中,使用线程提高CPU利用率,CPU线程调度程序中的不同线程来共同执行整个程序,并非让CPU线程独立执行程序.

(五)线程缺点

  • 性能损失

一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

  • 健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

  • 缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

  • 编程难度提高

编写与调试一个多线程程序比单线程程序困难得多

(六)线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃。
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。

(七)线程用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)。

二、Linux下的进程VS线程

(一)进程和线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程虽然共享进程数据,但也拥有自己的一部分数据:

线程ID
一组寄存器(保护CPU调度时存放在寄存器中的临时数据)
栈(保护线程运行时所形成的临时数据)
errno
信号屏蔽字
调度优先级

(二)进程的多个线程共享

因为进程和线程是在同一个地址空间的,因此代码段(Text Segment)、数据段(Data Segment)都是共享的:

  • 如果定义一个函数,在各线程中都可以调用
  • 如果定义一个全局变量,在各线程中都可以访问到

除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符
  • 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

(三)进程和线程的关系

在之前我们接触到的是单线程进程或者多个单线程进程。

  • 在linux中进程比线程安全的原因是每个进程有独立的虚拟地址空间,每个进程有自己独有的数据,具有独立性,不会数据共享这个说法是错误的,太过宽泛与片面.
  • 多进程之间的数据共享比多线程编程复杂,线程之间的通信简单(共享地址空间和页表信息,因此传参以及全局数据都可以实现通信),而不同进程之间的通信更为复杂,通常需要调用内核(系统调用)实现
  • 多线程的创建,切换,销毁速度快于多进程,因为线程之间共享了进程中的大部分资源,因此共享的数据不需要重新创建或销毁,因此消耗上低于进程,反之也就是速度快于进程.
  • 对于大量的计算优先使用多线程。大量的计算使用多进程和多线程都可以实现并行/并发处理,而线程的资源消耗小于多进程,而稳定相较多进程有所不如,因此还要看具体更加细致的需求场景
  • 一个进程至少有一个线程正确,但是“一个程序至少有一个进程”是错的,因为程序是静态的,不涉及进程,进程是程序运行时的实体,是一次程序的运行
  • 线程自己不拥有系统资源,因为 进程是资源的分配单位,所以线程并不拥有系统资源,而是共享使用进程的资源,进程的资源由系统进行分配
  • 任何一个线程都可以创建或撤销另一个线程。
    在这里插入图片描述

在这里插入图片描述

三、页表理解——虚拟到物理地址之间的转化

(一)页表理解

虚拟地址在被转化的过程中,不是直接转化的
虚拟地址是32位的:32bit 分成 10+10+12
0101 0101 00 0100 0111 11 0000 1110 0101
XXXX XXXX xx yyyy yyyy yy zzzz zzzz zzzz
虚拟地址的前10位在一级页表——页目录中找对应的二级页表;找到对应的二级页表后,中间10位在二级页表中找对应的page的起始地址(物理内存);找到对应的page的起始地址后,后12位作为偏移量在物理内存中的一个page(4KB)中找对应数据的地址,因为后12位有2^12=4096字节=4KB,正好物理内存管理单位是一个page,一个page是4KB,则后12位正好可以覆盖一个page的所有的地址。找到地址后CPU读取物理内存的数据。
在这里插入图片描述

(二)页表的好处

1.进程虚拟地址管理和内存管理,通过页表+page进行解耦

2.节省空间:分页机制+按需创建页表

页表也要占据内存,页表分离了,可以实现页表的按需创建,比如页目录的第3个地址从来没使用过,就可以不创建对应的二级目录,需要时再创建。一个页表大小是232/212=2^20字节(页目录和二级页表)

四、Linux线程控制

(一)POSXI线程库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文<pthread.h>
  • 要链接这些线程函数库时要使用编译器命令的“-lpthread”选项
    在这里插入图片描述
    ps -aL (all light)查看所有的轻量级进程
    LWP——light wait process:LWP就是轻量级进程,描述的是一个进程中的一个pcb
    在这里插入图片描述

1.错误检查:

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

2.线程创建:pthread_create

创建线程的函数叫做pthread_create
功能: 创建一个新的线程
函数原型如下:

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,创建失败返回错误码

3.获取进程ID:pthread_self

创建线程的函数叫做pthread_self()
功能: 获得该线程自身的ID
函数原型如下:

pthread_t pthread_self(void);

4.进程等待:pthread_join

功能:线程退出的时候,一般必须要进行join等待,如果不进行join,就会造成类似于进程那样的内存泄露问题。(即:作用是:释放线程资源——前提是线程退出了。并获取线程对应的退出码)
函数原型如下:

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

参数说明: thread:线程id
value_ptr:它指向一个指针,后者指向线程的返回值。(join 不需要退出信号)
返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的

终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数
    PTHREAD_ CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参
    数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

5.进程终止:pthread_exit

终止线程。线程终止——只考虑正常终止
(1)pthread_exit 对比 exit
exit(1):代表退出进程,任何一个 主/新线程调用exit,都表示整个进程退出。pthread_exit()仅仅是代表退出线程。

(2)线程退出有3种:

  1. 线程退出的方式,return —— return (void*)111;

  2. 线程退出的方式,pthread_exit —— pthread_exit((void*)1111);

  3. 线程退出的方式:线程取消请求,pthread_cancel —— pthread_ cancel(tid);
    void pthread_exit(void *retval); retval:线程退出码

6.pthread_cancel:进程取消

函数原型

int pthread_cancel(pthread_t thread); 

参数说明: thread:线程id

返回值:成功返回0;失败返回错误码

7.线程概念代码演示:

#include <iostream>
#include <cstdio>
#include <cassert>
#include <pthread.h>
#include <unistd.h>
using namespace std;

int g_val = 0;
string fun() {
    return "我是一个独立的方法!";
}
void *thread_routine(void *args) {
    const char* name = (const char *)args;
    while (true) {
        fun();
        cout << "我是新线程, 我正在运行! name: " << name << " : "<< fun()  << " : " << g_val++ << " &g_val : " << &g_val << endl;
        sleep(1);
    }
}
int main() {
    pthread_t tid;
    int n = pthread_create(&tid,nullptr,thread_routine,(void*)"thread one");
    assert(n == 0);
    (void)n;
    
    while (true) {
        char tidbuffer[64];
        snprintf(tidbuffer,sizeof(tidbuffer),"0x%x",tid);
        cout << "我是主线程, 我正在运行!, 我创建出来的线程的tid: " << tidbuffer << " : " << g_val << " &g_val : " << &g_val << endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

五、用户级线程概念

据操作系统内核是否对线程可感知,可以把线程分为内核线程和用户线程。
用户级线程由应用程序所支持的线程实现,内核意识不到用户级线程的实现。内核级线程又称为内核(系统)支持的线程。

(一)线程异常了怎么办?

  • 线程异常了——>整个进程整体异常退出。
  • 线程异常==进程异常
  • 线程会影响其他线程的运行——新线程会影响主线程main thread——健壮性/鲁棒性较低。

(二)理解 pthread_ t

本质上是一个地址。

  • 线程是一个独立的执行流
  • 线程一定会在自己的运行过程中,产生临时数据(调用函数,定义局部变量等)在新线程中修改全局变量后,新线程和主线程都能看到被修改后的结果
  • 线程一定需要有自己的独立的栈结构。

(三)线程栈

我们使用的线程库,用户级线程库,库的名字叫pthread。

1.代码区有三类代码:

  • 你自己写的代码。
  • 库的接口代码。(例如动态库libpthread. so会写入内存,通过页表映射到进程的共享区,代码区的库接口代码通过跳转到共享区执行完库中的代码,然后再跳转回代码区继续执行)。
  • 系统接口代码。(通过身份切换 用户—>内核 执行代码)。

所有的代码执行,都是在进程的地址空间当中进行执行的

2.解释 pthread_create 创建线程的返回值pthread_t

用户要用线程,但是OS没有线程的概念,libpthread. so线程库起承上启下的作用。
共享区内:
线程的全部实现,并没有全部体现在OS内,而是OS提供执行流,具体的线程结构由库来进行管理。库可以创建多个线程->库也要管理线程->管理:先描述,在组织

struct thread_ info
{ 
        pthread_ t tidh .
        void *stack; //私有栈
        ……
}  

libpthread. so线程库映射进共享区中。
创建线程时,线程库中也会创建一个 结构体struct thread_ info叫做线程控制块, 线程控制块内部是描述线程的信息,内部有一个指针指向mm_struct用户空间的一块空间——线程栈。
创建线程成功后,返回一个pthread_t类型的地址,pthread_t类型的地址保存着我们共享区中对应的用户级线程的线程控制块的起始地址!

在这里插入图片描述
结论:主线程的独立栈结构,用的就是地址空间中的栈区;新线程用的栈结构,用的是库中提供的栈结构(这个线程栈是库维护的,空间还是用户共享区提供的)

Linux中,线程库用户级线程库,和内核的LWP是1:1(
LWP(类比PID)——light wait process:轻量级进程编号。LWP=PID的执行流是主线程,俗称进程)

3.线程局部存储

线程库中的结构体struct thread_ info,内部是描述线程的信息,struct thread_ info中还有一个叫做线程局部存储的区域,作用:可以把全局变量私有化
在这里插入图片描述
正常情况全局变量是多个线程可以同时修改的:

加上__thread,把全局变量拷贝给每个进程各一份,使全局变量私有化,各自修改自己的全局变量。

六、线程分离

(一)概念

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

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离

int pthread_detach(pthread_self())

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

(二) 例子

1.pthread_ detach(pthread_ self()); 新线程自我分离

2.pthread_ detach(tid1);主线程分离新线程

(三) 线程分离可以理解为线程退出的第四种方式

  • 线程分离分为立即分离,延后分离,要保证线程还活着。线程分离意味着,我们不在关心这个线程的死活。线程分离可以理解为线程退出的第四种方式——延后退出。
  • 主线程的退出,并不会导致进程退出,也不会影响其他线程的运行。进程中的所有线程退出了,进程才会退出。—— 一般我们分离线程,对应的main thread不要退出(常驻内存的进程)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

字节跳动测试开发岗 3+1 面经+经验分享(收到offer,入职月薪27K)

现在&#xff0c;招聘黄金时间已经过了&#xff0c;在网上看了很多大佬的面经&#xff0c;也加了很多交流群&#xff0c;受到了很多朋友的提点&#xff0c;今天终于轮到我来分享面经啦&#xff0c;之前面试了几家公司&#xff0c;最后在八月初拿到了字节跳动测试岗的 offer&…

数据结构 -- AVL树

1、定义 平衡搜索二叉树&#xff0c;相对于搜索二叉树而言&#xff0c;AVL树又多了一个性质&#xff1a;左右子树的高度差不大于1. 2、平衡因子&#xff0c;balance factor&#xff0c;以下简称bf&#xff0c;是左子树高度减去右子树的高度 bf > 1&#xff0c;左边子树高bf …

Java最新版发送阿里短信教程

一、概述&#xff1a; 为什么现在的企业越来越多使用阿里云短信服务&#xff0c;究其原因是阿里云短信服务是一种可靠、高效、安全的短信发送服务&#xff0c;它具有以下优点&#xff1a; 高可靠性&#xff1a;阿里云短信服务采用全球领先的短信网关进行短信发送&#xff0c;确…

自定义线程池

自定义线程池原理 线程池中分为核心线程和临时线程&#xff1b;首先创建核心线程使用&#xff0c;创建之后一直存在线程池&#xff0c;核心线程被占用并且队列任务已满&#xff0c;才会创建临时线程&#xff1b;临时线程使用超过自定义临时线程最大数时会触发自定义的任务拒绝策…

你猜,一个TCP连接能发多少HTTP请求?

又见面了&#xff0c;我的网工朋友 曾经有这么一道经典面试题&#xff1a;从 URL 在浏览器被被输入到页面展现的过程中发生了什么&#xff1f; 相信大多数准备过的同学都能回答出来&#xff0c;但是如果继续问&#xff1a; 收到的 HTML 如果包含几十个图片标签&#xff0c;这…

【Vue】学习笔记-slot插槽

slot插槽 <slot>插槽&#xff1a;让父组件可以向子组件指定位置插入html结构&#xff0c;也是一种组件间通信的方式&#xff0c;适用于父组件>子组件 分类&#xff1a;默认插槽、具名插槽、作用域插槽 使用方式 a.默认插槽 b.具名插槽 父组件指明放入子组件的哪个插…

Django从Models 10分钟定制一个Admin后台

简介 Django自带一个Admin后台, 支持用户创建,权限配置和所有模型的增删改查功能, 只需要一些简单的配置就可快速得到一个开箱可用的后台管理系统 操作步骤 1. 更改设置,使用中文/亚洲时区 修改项目下django_shop目录下的settings.py文件 修改以下三行 LANGUAGE_CODE zh-h…

深度学习AI编译器-LLVM简介

1、什么是LLVM LLVM是一个编译器框架。LLVM作为编译器框架&#xff0c;是需要各种功能模块支撑起来的&#xff0c;你可以将clang和lld都看做是LLVM的组成部分&#xff0c;框架的意思是&#xff0c;你可以基于LLVM提供的功能开发自己的模块&#xff0c;并集成在LLVM系统上&…

干货 | 出国留学申请必备的6种材料,速来!!!

Hello,大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 我们又见面啦~你还好吗&#xff1f; 这是喵君姐姐的第n篇诚意推送~ 01 为什么要留学&#xff1f; 想去看外面的世界&#xff1f;想要打破科研的壁垒&#xff1f;想去更好的平台提升自己&#xff1f…

Android进阶之路 - 存、取、读 本地 Json 文件

最近在开发中又开始加载一些本地的json数据源&#xff0c;回头看之前竟然没记录&#xff0c;赶紧记录一波 ~ 如何准备一个合格的json文件?AndoridStudio中如何存放json文件&#xff1f;如何读取本地Json文件数据源?Java 版本Kotlin 版本 如何准备一个合格的json文件? 准备一…

GPT模型结合Python-GEE遥感云大数据分析、管理与可视化技术

查看原文>>>GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例应用 目录 第一章、理论基础 第二章、开发环境搭建 第三章、遥感大数据处理基础与ChatGPT等AI模型交互 第四章、典型案例操作实践 第五章、输入输出及数据资产高效管理 第…

dvwa靶场通关(三)

第三关&#xff1a;CSRF&#xff08;跨站请求伪造&#xff09; csrf跨站请求伪造&#xff1a;是一种对网站的恶意利用。尽管听起来像跨站脚本&#xff0c;但它与xss非常不同&#xff0c;xss利用站点内受信任用户&#xff0c;而csrf则通过伪造来自受信任用户的请求来利用受信任…

Springboot +spring security,认证方式---Form表单认证的实现(二)

一.简介 这篇文章来学习下security的认证方式其中的Form表单认证 二.Spring Security的认证方式 2.1什么是认证 认证: 就是用来判断系统中是否存在某用户&#xff0c;并判断该用户的身份是否合法的过程&#xff0c;解决的其实是用户登录的问题。认证的存在&#xff0c;是为…

【Java-10】深入浅出线程安全、死锁、状态、通讯、线程池

主要内容 线程安全线程死锁线程的状态线程间通讯线程池 1 线程安全 1.1 线程安全产生的原因 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据错乱就是线程的安全问题了 问题出现的原因 : 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据…

有哪些辅助计算机开发的工具推荐?

以下是一些辅助计算机开发的工具推荐&#xff1a; 集成开发环境&#xff08;Integrated Development Environment&#xff0c;IDE&#xff09;&#xff1a; 常用的IDE包括Visual Studio、Eclipse、IntelliJ IDEA、PyCharm等&#xff0c;它们提供了代码编辑器、调试器、构建工…

TDengine 集成 EMQX 通过规则引擎实现设备数据直接入库

背景 曾使用过 IoTDB 自带的 MQTT Broker 实现了设备数据入库&#xff0c;那么使用 TDengine 时&#xff0c;我们可以借助 EMQX &#xff08;一款优秀的国产开源 MQTT Broker &#xff09;的规则引擎结合 TDengine 的 RESTful API 完成设备数据的路由与入库。 用到的工具 TD…

chatgpt赋能python:Python下载Module的指南

Python下载Module的指南 作为一门高级编程语言&#xff0c;Python凭借其简单易学、高效便捷的特点&#xff0c;越来越受到广大程序员的喜爱。Python社区也逐渐发展壮大&#xff0c;丰富的第三方Module为我们提供了更多功能强大、用途广泛的工具。本篇文章将介绍Python下载Modu…

从汇编代码的角度去理解C++多线程编程问题

目录 1、多线程问题实例 2、理解该多线程问题的预备知识 2.1、二进制机器码和汇编代码 2.2、多线程切换与CPU时间片 2.3、多线程创建与线程函数 3、从汇编代码的角度去理解多线程问题 4、问题解决办法 5、熟悉汇编代码有哪些用处&#xff1f; 5.1、在代码中插入汇编代…

信号处理与分析-傅里叶

目录 一、引言 二、傅里叶级数 1. 傅里叶级数的定义 2. 傅里叶级数的性质 三、傅里叶变换 1. 傅里叶变换的定义 2. 傅里叶变换的性质 四、离散傅里叶变换 1. 离散傅里叶变换的定义 2. 离散傅里叶变换的性质 五、应用实例 1. 信号处理 2. 图像处理 六、总结 一、引…

Revit中窗族的默认窗台高度与底高度是一样?

​  一、窗族的默认窗台高度与底高度是一样的吗? 窗族的系统设定中有一个自带的参数就是默认窗台高度&#xff0c;指的是窗户放置的时候窗户最底端离墙的最底端高度。 当我们创建一个建筑样板将我们创建好的窗族放置好的时候&#xff0c;这个参数就在窗的类型属性中&#xf…