【探索Linux】—— 强大的命令行工具 P.19(多线程 | 线程的概念 | 线程控制 | 分离线程)

在这里插入图片描述

阅读导航

  • 引言
  • 一、 Linux线程概念
    • 1. 什么是线程
    • 2. 线程的概念
    • 3. 线程与进程的区别
    • 4. 线程异常
  • 二、Linux线程控制
    • 1. POSIX线程库
    • 2. 创建线程 pthread_create() 函数
      • (1)头文件
      • (2)函数原型
      • (3)参数解释
      • (4)返回值
      • (5)使用示例
    • 3. 线程ID及进程地址空间布局
      • (1)进程地址空间布局
      • (2)线程ID pthread_self() 函数
    • 4. 线程等待 pthread_join() 函数
      • (1)头文件
      • (2)函数原型
      • (3)参数解释
      • (4)返回值
      • (5)使用示例
    • 5. 线程终止
      • (1)线程终止的三种方法
      • (2)pthread_exit() 函数
      • (3)pthread_cancel() 函数
  • 三、分离线程
    • 1. joinable与线程分离
    • 2. 分离线程 pthread_detach() 函数
      • (1)头文件
      • (2)函数原型
      • (3)参数解释
      • (4)返回值
      • (5)使用示例
  • 四、线程的优缺点
  • 五、线程用途
  • 温馨提示

引言

在当今信息技术日新月异的时代,多线程编程已经成为了日常开发中不可或缺的一部分。Linux作为一种广泛应用的操作系统,其对多线程编程的支持也相当完善。本文将会介绍关于Linux多线程相关的知识,其中包括了线程的概念、线程控制、线程分离等方面的内容。如果你希望提升自己的多线程编程能力,本文将为你提供实用的技术指导和详尽的知识储备。让我们一起来深入了解Linux多线程编程的奥秘吧!

一、 Linux线程概念

1. 什么是线程

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

2. 线程的概念

在Linux中,线程是指在同一个进程内部并发执行的多个子任务。Linux将线程作为轻量级进程(LWP)来实现,每个线程共享相同的进程地址空间和其他资源,包括文件描述符、信号处理器和其他内核状态。由于线程之间共享进程的资源,因此线程之间的切换开销通常比进程之间的切换要小得多。

Linux使用POSIX线程库(pthread)来支持多线程编程。通过pthread库,开发人员可以方便地创建、控制和同步线程,实现多线程编程的各种功能。在Linux中,线程的创建和管理都是通过系统调用和pthread库来完成的,开发人员可以使用pthread_create()函数创建新线程,并使用pthread_join()函数等来等待线程的结束,后面我们会详细介绍。

在Linux中,线程与进程一样拥有自己的ID、寄存器上下文、栈和线程本地存储(TLS)。线程可以通过共享内存进行通信,也可以使用线程同步机制来协调彼此的操作。在多核处理器上,Linux内核会将不同的线程分配到不同的处理器核心上并行执行,以提高系统的性能和响应速度。

总的来说,Linux线程是在同一个进程内并发执行的多个子任务,通过共享进程的资源和使用pthread库来实现线程的创建和管理。在Linux环境下,充分利用线程可以提高程序的并发能力和性能表现。
在这里插入图片描述

3. 线程与进程的区别

Linux线程和进程的主要区别在于它们是操作系统对应不同的执行单元。

  1. 资源分配

    • 进程是资源分配的最小单位,每个进程都有自己的地址空间、全局变量、堆栈、文件描述符等系统资源。
    • 线程是CPU调度的最小单位,多个线程可以共享同一个进程的资源,包括地址空间、全局变量、文件描述符等。
  2. 切换开销

    • 因为线程共享进程资源,因此线程之间的切换开销比进程之间的切换要小得多。
    • 线程的上下文切换只需要保存处理器寄存器和栈指针,而进程切换需要保存整个进程的上下文信息,包括内存映像、堆栈、寄存器等。
  3. 通信机制

    • 进程之间通常使用IPC(Inter-Process Communication)机制来进行进程间通信,例如管道、消息队列、共享内存和信号量等。
    • 线程之间可以通过共享内存、互斥锁、条件变量等同步机制来进行通信和协调。
  4. 并发能力

    • 由于线程共享进程资源和较小的切换开销,因此线程可以更轻松地实现并发执行,提高程序的并发处理能力和性能表现。
  5. 安全性

    • 线程共享进程资源,因此线程之间操作共享数据可能会引起竞态条件等并发问题,需要使用同步机制来协调线程的操作。
    • 进程之间不共享地址空间,可以通过IPC机制来实现安全的进程间通信。

进程和线程的关系如下图
在这里插入图片描述

4. 线程异常

  1. 线程死锁:线程之间相互等待对方释放资源,导致所有线程都无法继续执行的情况。

  2. 竞态条件:多个线程同时访问共享的资源,导致意外的结果或者数据损坏。

  3. 内存泄漏:线程未正确释放动态分配的内存,导致系统资源耗尽。

  4. 线程间通信问题:线程之间的通信出现问题,例如数据丢失、阻塞等情况。

  5. 未捕获的异常:线程中的代码抛出未捕获的异常,导致线程意外终止。

当一个线程出现严重的异常导致崩溃时,会触发进程内的异常处理机制。在大多数操作系统中,异常会导致信号被发送给进程,例如SIGSEGV(段错误)或SIGFPE(浮点异常)。默认情况下,这些信号会终止整个进程。

🚨注意线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出

二、Linux线程控制

1. POSIX线程库

在Linux系统中,POSIX线程库(也称为pthread库)是一套用于多线程编程的标准接口。它基于POSIX标准(Portable Operating System Interface)定义了一组函数和数据类型,使得开发者可以方便地进行多线程程序的开发

POSIX线程库的设计目标是提供一个可移植、高效和可靠的多线程编程接口。它已经成为类UNIX系统上标准的多线程编程接口,并在Linux系统中得到广泛应用。开发者可以使用POSIX线程库编写具有良好可移植性的多线程程序,无需关心底层操作系统的差异

2. 创建线程 pthread_create() 函数

在Linux系统中,线程的创建是通过POSIX线程库(pthread库)提供的函数来实现的

(1)头文件

pthread_create() 函数的使用需要包含pthread库的头文件pthread.h

#include <pthread.h>

(2)函数原型

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

(3)参数解释

  1. thread:指向 pthread_t 类型的指针,用于存储新线程的标识符。在函数成功返回时,该指针被设置为新线程的标识符,可以用于后续操作。

  2. attr:指向 pthread_attr_t 类型的指针,用于设置线程的属性,通常可以传入 nullptr,表示使用默认属性。如果需要设置线程的属性,可以使用 pthread_attr_init() 函数初始化属性对象,并使用 pthread_attr_setxxx() 函数设置属性。

  3. start_routine:指向线程函数的指针,即新线程的执行体。该函数应该以 void* func(void*) 的形式定义,即带有一个 void 类型指针参数,返回一个 void 类型指针。线程函数的返回值将作为线程的退出状态,可以通过 pthread_join() 函数获取。

  4. arg:传递给线程函数的参数,它必须是一个 void 类型指针,开发者需要自行处理类型转换。

(4)返回值

  • 如果成功创建了新线程,则函数返回 0;
  • 如果失败,则返回一个非零值,表示出现了错误。在出错的情况下,可以使用 perror() 函数输出错误信息,也可以使用 strerror() 函数获取错误信息。

(5)使用示例

#include <stdio.h>
#include <pthread.h>void* thread_function(void* arg) {int tid = *(int*)arg;printf("This is thread %d.\n", tid);pthread_exit(NULL);
}int main() {pthread_t tid[3];int i, rc;// 创建三个新线程for (i = 0; i < 3; i++) {rc = pthread_create(&tid[i], NULL, thread_function, (void*)&i);if (rc != 0) {fprintf(stderr, "Failed to create new thread.\n");return 1;}}// 等待所有新线程结束for (i = 0; i < 3; i++) {rc = pthread_join(tid[i], NULL);if (rc != 0) {fprintf(stderr, "Failed to join the thread.\n");return 1;}}printf("All threads exit.\n");return 0;
}

通过上面的步骤,可以在 Linux 系统下成功创建并执行新的线程。需要注意的是,调pthread_create() 函数时传递给线程函数的参数必须是指向整型变量的指针,否则可能会出现不可预期的错误

在这里插入图片描述

3. 线程ID及进程地址空间布局

(1)进程地址空间布局

进程地址空间布局是指操作系统在内存中为每个进程分配的地址空间的布局方式。以下是典型的Linux进程地址空间布局:

  1. 代码段(Text Segment):
    代码段存储了可执行程序的机器指令。它通常是只读的,并且在内存中只有一份,用于所有执行该程序的进程。

  2. 数据段(Data Segment):
    数据段存储了全局变量和静态变量。它包括了初始化的数据和非初始化的BSS段(Block Started by Symbol)。数据段通常是可读写的。

  3. 堆(Heap):
    堆是动态分配内存的区域。在运行时,通过调用malloc()calloc()等函数分配堆内存。堆的大小不固定,可以根据需要动态增长或缩小。

  4. 栈(Stack):
    栈用于存储函数调用、局部变量和函数参数等信息。每个线程都有自己的栈,用于保存线程特定的上下文信息。栈的大小通常是固定的。

  5. 共享库(Shared Libraries):
    共享库存储了被多个进程共享的代码和数据。它们被加载到内存中,并映射到每个进程的地址空间中。

  6. 内核空间(Kernel Space):
    内核空间是由操作系统内核使用的内存区域,不属于进程的地址空间。它包括操作系统内核的代码和数据结构。

在这里插入图片描述

(2)线程ID pthread_self() 函数

线程ID(Thread ID)是操作系统分配给每个线程的唯一标识符。在不同的操作系统中,线程ID的表示方式和取值范围可能会有所不同。
pthread_self()函数是一个POSIX线程库中的函数,用于获取当前线程的线程ID。它的原型如下:

pthread_t pthread_self(void);

该函数没有参数,返回类型为pthread_t,即线程ID的类型

使用pthread_self()函数可以在多线程程序中获取当前线程的线程ID。每个线程在创建时都会被分配一个唯一的线程ID,可以通过该ID来标识和区分不同的线程。

下面是一个简单的示例代码,演示了如何使用pthread_self()函数获取当前线程的线程ID:

#include <stdio.h>
#include <pthread.h>void* thread_func(void* arg) {pthread_t tid = pthread_self();printf("Thread ID: %lu\n", tid);return NULL;
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, NULL);return 0;
}

在上述示例中,主线程创建了一个新线程,并通过pthread_create()函数启动线程执行thread_func()函数。在thread_func()函数中,调用pthread_self()函数获取当前线程的线程ID,并将其打印输出。

🚨注意线程ID的类型pthread_t可能是一个不透明的数据类型,具体实现取决于操作系统和编译器。在上述示例中,使用%lu格式指定符打印无符号长整型,以与pthread_t类型匹配。在不同的系统和编译环境中,可能需要根据具体情况调整打印格式。
在这里插入图片描述

4. 线程等待 pthread_join() 函数

线程等待是一种同步机制,会导致线程之间的阻塞和等待。在设计多线程程序时,需要合理地安排线程的执行顺序和等待关系,以避免死锁、饥饿等问题pthread_join()函数是一个POSIX线程库中的函数,用于等待指定的线程结束并回收其资源。

(1)头文件

pthread_join() 函数的使用需要包含pthread库的头文件pthread.h

#include <pthread.h>

(2)函数原型

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

(3)参数解释

  • thread参数是要等待的目标线程的线程ID,
  • retval参数用于接收目标线程的返回值(如果有)。pthread_join()函数会阻塞调用线程,直到目标线程结束为止,并且可以获取目标线程的返回值

(4)返回值

pthread_join() 函数的返回值表示线程的终止状态,具体取值如下:

  • 如果线程的返回值已经被存放到 value_ptr 指向的内存中,则返回 0。
  • 如果指定的线程在执行过程中被取消,则返回 PTHREAD_CANCELED
  • 如果调用该函数时出现错误,则返回相应的错误代码。

(5)使用示例

下面是一个简单的示例代码,演示了如何使用pthread_join()函数等待子线程结束并获取其返回值:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>void* thread_func(void* arg) {int* result = (int*)malloc(sizeof(int));*result = 42;printf("Thread is about to exit\n");pthread_exit((void*)result);  // 终止线程,并返回 result
}int main() {pthread_t tid;void* ret_val;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, &ret_val);  // 获取线程的返回值if (ret_val) {printf("Thread returned: %d\n", *((int*)ret_val));free(ret_val);  // 释放返回值对应的内存} else {printf("Thread returned NULL\n");}return 0;
}

在上面的示例中,我们创建了一个新线程,线程函数中使用了 pthread_exit() 函数来结束线程并返回一个整数值。在主线程中,我们通过 pthread_join() 函数等待线程的终止,并获取了线程的返回值。

🚨注意pthread_join()函数会使调用线程进入阻塞状态,直到目标线程结束。如果不关心目标线程的返回值,也可以将retval参数设置为NULL。另外,在多线程程序中,需要特别注意线程的安全退出和资源回收,以避免产生悬挂线程或资源泄漏的问题。
在这里插入图片描述

5. 线程终止

(1)线程终止的三种方法

  1. 线程函数返回:线程函数执行完毕并从函数中返回,线程会自动终止。线程函数可以通过返回一个值来传递结果给线程的创建者。

  2. 调用 pthread_exit() 函数:线程可以显式地调用 pthread_exit() 函数来终止自己。这个函数接受一个参数作为线程的返回值,可以被其他线程通过调用 pthread_join() 函数获取。

  3. 取消线程:线程可以被其他线程取消。调用 pthread_cancel(pthread_t thread) 函数可以请求取消指定的线程。被取消的线程可以选择在适当的时机终止自己,或者忽略取消请求继续执行。

(2)pthread_exit() 函数

pthread_exit() 函数是用于终止当前线程并返回一个值的 POSIX 线程库函数。该函数的原型如下所示:

void pthread_exit(void* value_ptr);
  • value_ptr:表示线程的返回值,可以是任意类型的指针。当线程调用 pthread_exit() 函数时,会将 value_ptr 指向的内容作为线程的返回值。

pthread_exit() 函数允许线程在执行过程中随时退出,并返回一个值。这个返回值可以被其他线程通过调用 pthread_join() 函数获取,从而实现线程间的数据交互和结果传递。

下面是一个简单的示例,演示了如何在线程中使用 pthread_exit() 函数来结束线程并返回一个值:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>void* thread_func(void* arg) {int* result = (int*)malloc(sizeof(int));*result = 42;printf("Thread is about to exit\n");pthread_exit((void*)result);  // 终止线程,并返回 result
}int main() {pthread_t tid;void* ret_val;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, &ret_val);  // 获取线程的返回值printf("Thread returned: %d\n", *((int*)ret_val));free(ret_val);  // 释放返回值对应的内存return 0;
}

在上面的示例中,我们创建了一个新线程,线程函数中使用了 pthread_exit() 函数来结束线程并返回一个整数值。在主线程中,我们通过 pthread_join() 函数获取了线程的返回值,并打印输出了该值。

(3)pthread_cancel() 函数

pthread_cancel() 函数是 POSIX 线程库中用于取消指定线程的函数。该函数的原型如下所示:

int pthread_cancel(pthread_t thread);
  • thread:表示要取消的线程的标识符。

pthread_cancel() 函数会向指定线程发送一个取消请求,并尝试终止该线程的执行。但是,线程是否会被成功取消取决于多个因素,包括线程自身的取消状态和取消点的设置。

pthread_cancel() 函数只是发送一个取消请求并立即返回,并不能保证目标线程会立即终止。目标线程可以选择忽略取消请求或者在适当的取消点进行处理。

取消点(cancellation point)是线程中的一些特定位置,线程在这些位置上可以检查是否有取消请求,并决定是否终止自己的执行。常见的取消点包括 I/O 操作、阻塞的系统调用等。

如果目标线程成功响应了取消请求,并在取消点终止了执行,那么取消状态将被设置为已取消。可以通过调用 pthread_setcancelstate()pthread_setcanceltype() 函数来控制线程的取消状态和取消类型。

下面是一个示例,演示了如何使用 pthread_cancel() 函数来取消线程的执行:

#include <stdio.h>
#include <pthread.h>void* thread_func(void* arg) {while (1) {// 线程执行的逻辑}return NULL;
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);// 在主线程中取消子线程的执行pthread_cancel(tid);pthread_join(tid, NULL);  // 等待线程结束return 0;
}

在上面的示例中,我们创建了一个新线程,并在主线程中调用 pthread_cancel() 函数来取消子线程的执行。接着,我们使用 pthread_join() 函数等待子线程的终止。

🚨注意在使用 pthread_cancel() 函数取消线程时,应确保目标线程处于可取消状态,并且在适当的位置设置了取消点。否则,取消请求可能被忽略,导致线程无法正确终止。

三、分离线程

1. joinable与线程分离

“joinable” 和线程分离是两种不同的线程状态。

  • “joinable” 状态的线程是指可以被其他线程显式等待和回收资源的线程。在 POSIX 线程库中,默认情况下,创建的线程是可连接状态(joinable)。可连接状态的线程需要使用 pthread_join() 函数来等待其终止,并获取其返回值(如果有)。
  • 线程分离是指将线程属性设置为分离状态,使得线程在终止时可以自动释放相关资源,而无需等待其他线程显式对其进行回收。分离线程通常用于不需要获取线程返回值或进行线程同步的场景。

2. 分离线程 pthread_detach() 函数

pthread_detach() 函数是 POSIX 线程库中的一个函数,用于将指定线程设置为分离状态,即使该线程在终止时可以自动释放相关资源,而无需等待其他线程显式对其进行回收。

(1)头文件

pthread_detach() 函数的使用需要包含pthread库的头文件pthread.h

#include <pthread.h>

(2)函数原型

int pthread_detach(pthread_t thread);

(3)参数解释

  • thread:表示要设置为分离状态的线程的标识符。

(4)返回值

pthread_detach() 函数的返回值为 0 表示调用成功,返回值为非零表示调用失败。失败的原因可能是参数不正确或者内部出现了错误。

🚨注意线程在被设置为分离状态之前,必须处于可连接状态。否则,pthread_detach() 函数将无法将其设置为分离状态,并返回一个错误码

(5)使用示例

下面是一个示例,演示了如何使用 pthread_detach() 函数将线程设置为分离状态:

#include <stdio.h>
#include <pthread.h>void* thread_func(void* arg) {// 线程执行的逻辑return NULL;
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);// 将线程设置为分离状态pthread_detach(tid);// 不需要使用 pthread_join() 函数进行回收return 0;
}

在上面的示例中,我们创建了一个新线程,并使用 pthread_detach() 函数将其设置为分离状态。由于该线程已经处于分离状态,因此在主线程中无需使用 pthread_join() 函数进行回收。

四、线程的优缺点

线程是一种轻量级的执行单元,可以在一个进程内并发执行多个任务。线程有以下优点和缺点:

优点

  1. 并发执行:线程允许多个任务同时执行,提高了程序的运行效率和响应速度。可以充分利用多核处理器的计算能力。
  2. 共享数据:线程可以共享同一个进程的内存空间,方便数据之间的共享和通信。不同线程之间可以直接读取和修改同一块内存区域的数据,简化了多任务编程的复杂性。
  3. 资源高效:线程的创建和销毁消耗的资源相对较少,线程切换的开销也较小。相比于进程,线程更加轻量级。
  4. 逻辑清晰:使用线程可以将程序分解成多个独立的执行单元,每个线程负责不同的任务,使得程序的逻辑结构更加清晰、模块化。

缺点

  1. 同步和共享问题:多个线程访问共享数据时需要进行同步操作,以避免数据竞争和不一致的结果。需要使用锁、信号量等机制来保护共享资源,增加了编程的复杂性。
  2. 错误管理困难:线程共享同一进程的内存空间,一个线程对共享资源的错误操作可能会影响其他线程的正常执行,导致难以追踪和调试错误。
  3. 调试和测试复杂:由于线程并发执行,线程之间的交互和调试相对复杂。当程序出现问题时,需要仔细分析各个线程的执行顺序和交互情况,增加了调试和测试的难度。
  4. 资源竞争:多个线程同时访问共享资源时可能引发资源竞争问题,如死锁、饥饿等。需要合理设计和管理线程的同步和互斥机制,以避免资源竞争问题。

五、线程用途

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

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

Spring MVC学习随笔-控制器(Controller)开发详解:控制器跳转与作用域(一)

学习视频&#xff1a;孙哥说SpringMVC&#xff1a;结合Thymeleaf&#xff0c;重塑你的MVC世界&#xff01;&#xff5c;前所未有的Web开发探索之旅 第五章、SpringMVC控制器开发详解 三 5.1 核心要点 3.流程跳转 5.2 JavaWeb中流程跳转的核心回顾 5.2.1 JavaWeb中流程跳转的核…

网络入门---网络编程初步认识和实践

目录标题 前言准备工作udpserver.hpp成员变量构造函数初始化函数(socket,bind)start函数(recvfrom) udpServer.ccudpClient.hpp构造函数初始化函数run函数(sendto) udpClient.cc测试 前言 在上一篇文章中我们初步的认识了端口号的作用&#xff0c;ip地址和MAC地址在网络通信时…

QT 中 QProgressDialog 进度条窗口 备查

基础API //两个构造函数 QProgressDialog::QProgressDialog(QWidget *parent nullptr, Qt::WindowFlags f Qt::WindowFlags());QProgressDialog::QProgressDialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *…

面试 Java 基础八股文十问十答第三期

面试 Java 基础八股文十问十答第三期 作者&#xff1a;程序员小白条&#xff0c;个人博客 ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 21.说下Java8的Stream流的常用方法 答: forEach遍历、find、match进行匹配reduce进行归约&#xff0c;比如求和&#xff0c;乘&#xff0c;除聚合…

淘宝用户体验VOC标签体系

本专题共10篇内容&#xff0c;包含淘宝APP基础链路过去一年在用户体验数据科学领域&#xff08;包括商详、物流、性能、消息、客服、旅程等&#xff09;一些探索和实践经验。 在商详页基于用户动线和VOC挖掘用户决策因子带来浏览体验提升&#xff1b;在物流侧洞察用户求助时间与…

计算机组成原理笔记——存储器(静态RAM和动态RAM的区别,动态RAM的刷新, ROM……)

■ 随机存取存储器 ■ 1.随机存取存储器&#xff1a;按存储信息的原理不同分为&#xff1a;静态RAM和动态RAM 2.静态RAM&#xff08;SRAM&#xff09;&#xff1a;用触发器工作原理存储信息&#xff0c;但电源掉电时&#xff0c;存储信息会丢失具有易失性。 3.存储器的基本单元…

springboot监听器模式源码精讲

1.前言 很多时候我们看源码的时候看不下去&#xff0c;其中一个原因是系统往往使用了许多设计模式&#xff0c;如果你不清楚这些设计模式&#xff0c;这无疑增加了你阅读源码的难度。 springboot中就大量使用了设计模式&#xff0c;本文主要介绍其中的一种监听器模式&#xf…

数学建模-基于BL回归模型和决策树模型对早产危险因素的探究和预测

整体求解过程概述(摘要) 近年来&#xff0c;全球早产率总体呈上升趋势&#xff0c;在我国&#xff0c;早产儿以每年 20 万的数目逐年递增&#xff0c;目前早产已经成为重大的公共卫生问题之一。据研究,早产是威胁胎儿及新生儿健康的重要因素&#xff0c;可能会造成死亡或智力体…

深度学习 -- 神经网络

1、神经网络的历史 2、 M-P模型 M-P模型是首个通过模仿神经元而形成的模型。在M-P模型中,多个输入节点对应一个输出节点y。每个输入x,乘以相应的连接权重w,然后相加得到输出y。结果之和如果大于阈值h,则输出1,否则输出0。输入和输出均是0或1。 公式2.1&#xff1a; …

Redis 安装部署

文章目录 1、前言2、安装部署2.1、单机模式2.1.1、通过 yum 安装&#xff08;不推荐&#xff0c;版本老旧&#xff09;2.1.1、通过源码编译安装&#xff08;推荐&#xff09; 2.2、主从模式2.3、哨兵模式2.4、集群模式2.5、其他命令2.6、其他操作系统 3、使用3.1、Java 代码 —…

神经网络中的 Grad-CAM 热图(Gradient-weighted Class Activation Mapping)

Grad-CAM&#xff08;Gradient-weighted Class Activation Mapping&#xff09;是一种用于可视化卷积神经网络&#xff08;CNN&#xff09;中特定类别的激活区域的方法。其基本思想是使用网络的梯度信息来获取关于特定类别的空间定位信息。 Grad-CAM 的具体公式如下&#xff1…

Python逐步打造惊艳的折线图

大家好&#xff0c;Matplotlib可以快速轻松地使用现成的函数绘制图表&#xff0c;但是微调步骤需要花费更多精力。今天本文将介绍如何使用Matplotlib绘制吸引人的图表&#xff0c;实现折线图的惊艳变身。 1.数据 为了说明方法&#xff0c;本文使用了包含过去50年各国GDP信息的…

QT 中 QTimer 类 备查

基础 // 指定了父对象, 创建的堆内存可以自动析构 QTimer::QTimer(QObject *parent nullptr);// 根据指定的时间间隔启动或者重启定时器, 需要调用 setInterval() 设置时间间隔 void QTimer::start();// 启动或重新启动定时器&#xff0c;超时间隔为msec毫秒。 void QTimer::…

韩语语法中에和로/으로区别,柯桥发音入门韩语培训学校

에和로/으로在行动的去向与到达或涉及的地点一致时&#xff0c;二者可以互换。 但是에表示到达或涉及的具体地点&#xff0c;而로/으로表示的时动作指向的方向或经过的地点。 在只表示去向而不表示具体地点时&#xff0c;只能用로/으로&#xff0c;而在只表示具体地点而不表示方…

2023.12.2 做一个后台管理网页(左侧边栏实现手风琴和隐藏/出现效果)

2023.12.2 做一个后台管理网页&#xff08;左侧边栏实现手风琴和隐藏/出现效果&#xff09; 网页源码见附件&#xff0c;比较简单&#xff0c;之前用很多种方法实现过该效果&#xff0c;这次的效果相对更好。 实现功能&#xff1a; &#xff08;1&#xff09;实现左侧边栏的手…

摩根士丹利:人工智能推动增长

摩根士丹利&#xff08;NYSE&#xff1a;MS&#xff09;将人工智能战略整合到其财富管理业务中&#xff0c;标志着竞争性金融格局迈出了变革性的一步。该公司的人工智能计划&#xff0c;包括与 OpenAI 合作开发人工智能聊天机器人&#xff0c;促进了其财富部门的显着增长。值得…

【数据库】数据库基于封锁机制的调度器,使冲突可串行化,保障事务和调度一致性

封锁使可串行化 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期更…

Linux查看计算机处理器相关的信息

采用命令lscpu。部分结果如下&#xff1a;

MicroPython标准库

MicroPython标准库 arraybinascii(二进制/ASCII转换)builtins – 内置函数和异常cmath – 复数的数学函数collections – 集合和容器类型errno – 系统错误代码gc – 控制垃圾收集器hashlib – 散列算法heapq – 堆队列算法io – 输入/输出流json – JSON 编码和解码math – 数…

详解Spring中BeanPostProcessor在Spring工厂和Aop发挥的作用

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…