【探索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++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

初识消息队列

1、消息 消息&#xff08;Message&#xff09;是指在应用间传送的数据。消息可以非常简单&#xff0c;比如只包含文本字符串&#xff0c;也可以更复杂&#xff0c;可能包含嵌入对象。 2、消息队列 消息队列&#xff08;Message Queue&#xff09;是一种应用间的通信方式&#…

力扣题:字符串的反转-11.23

力扣题-11.23 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;557. 反转字符串中的单词 III 解题思想&#xff1a;先读取单词&#xff0c;然后将单词进行翻转即可 class Solution(object):def reverseWords(self, s):""":type s…

C语言-指针_02

指针-02 1. 指针的指针 概念&#xff1a;指针变量中存储的是指针的地址&#xff0c;又名 二维指针 语法&#xff1a; 数据类型 **p;示例&#xff1a; #include <stdio.h> int main(int argc, char const *argv[]) {int num 10;int *p1 &num;int **p2 &p1…

win11 无法登录微软账户 终极解决方案

背景&#xff1a;win11突然无法登录微软账户&#xff0c;office无法激活&#xff0c;Edge里的微软账户也无法登录&#xff0c;反馈中心也无法打开等&#xff0c;有网络&#xff0c;浏览器可以访问微软并进行登录。 试过网上的网络配置&#xff08;SSL及TLS协议勾选&#xff09…

论文编写软件latex安装教程

目录 1.下载安装包2.安装texlive 本人系统为windows&#xff0c;本教程基于windows系统&#xff0c;如果是其它系统请参考对应教程&#xff0c;注意选择对应系统的安装包&#xff01; 1.下载安装包 有三种集成环境安装包 texlive 是主流的环境&#xff0c;集成了较多的包&…

k8s(三): 基本概念-ReplicaSet与Deployment

PeplicaSet ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合&#xff0c;通常用来保证给定数量的、完全相同的 Pod 的可用性。 最佳实践 Deployment 是一个可以拥有 ReplicaSet 并使用声明式方式在服务器端完成对 Pod 滚动更新的对象。 尽管 Rep…

华为杯研究生数学建模优秀参考论文(优秀论文参考2004-2022年)

一、背景介绍 中国研究生数学建模竞赛是一项面向在校研究生进行数学建模应用研究的学术竞赛活动&#xff0c;是广大在校研究生提高建立数学模型和运用互联网信息技术解决实际问题能力&#xff0c;培养科研创新精神和团队合作意识的大平台&#xff0c;大赛赞助单位为华为技术有限…

<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法

目录 一、synchronized 关键字简介 二、synchronized 的特点 -- 互斥 三、synchronized 的特点 -- 可重入 四、synchronized 的使用示例 4.1 修饰代码块 - 锁任意实例 4.2 修饰代码块 - 锁当前实例 4.3 修饰普通方法 - 锁方法所在实例 4.4 修饰代码块 - 锁指定类对象 …

【开源】基于JAVA语言的校园疫情防控管理系统

项目编号&#xff1a; S 037 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S037&#xff0c;文末获取源码。} 项目编号&#xff1a;S037&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、…

【数据结构】二叉树---C语言版

二叉树 一、树的概念及结构1.树的概念2.树的相关概念3.树的表示4.树在实际中的应用 二、二叉树的概念及结构1.二叉树的概念2.满二叉树3.完全二叉树4.二叉树的性质5.二叉树的储存结构 三、二叉树的遍历1.前序遍历2.中序遍历3.后序遍历4.层序遍历 四、手撕二叉树&#xff08;务必…

基于JSP的网络考试系统/在线考试系统的设计与实现

摘 要 网络考试系统是由高校的一个网络考试&#xff0c;按照章程自主开展网络考试系统。网络考试是实施素质教育的重要途径和有效方式&#xff0c;在加强校园文化建设、提高学生综合素质、引导学生适应社会、促进学生成才就业等方面发挥着重要作用&#xff0c;是新形势下有效凝…

YOLOv7+姿态估计Pose+tensort部署加速

YOLOv7-Pose 实现YOLOv7&#xff1a;可训练的免费套件为实时目标检测设置了最新技术标准 YOLOv7-Pose的姿态估计是基于YOLO-Pose的。关键点标签采用MS COCO 2017数据集。 训练 使用预训练模型yolov7-w6-person.pt进行训练。训练命令如下&#xff1a; python -m torch.distr…

leetcode 3. 无重复字符的最长子串

代码&#xff1a; //采用滑动窗口来进行解决 class Solution {public int lengthOfLongestSubstring(String s) {//字符串由英文字母、数字、符号和空格组成&#xff0c;通过对应的 ASCLL 码作为下标在数组中记录出现的次数//判断字符在字串中是否重复出现int[] ascllnew int[…

前端对浏览器的理解

浏览器的主要构成 用户界面 &#xff0d; 包括地址栏、后退/前进按钮、书签目录等&#xff0c;也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。 浏览器引擎 &#xff0d; 用来查询及操作渲染引擎的接口。 渲染引擎 &#xff0d; 用来显示请求的内容&#…

深入理解同源限制:网络安全的守护者(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

我爱上这38个酷炫的数据大屏(附 Python 源码)

随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。 可视化大屏不再只是电影里奇幻的画面&#xff0c;而是被实实在在地应用在政府、商业、金融、制造等各个行业的业务场景中&#xff0c;切切实实地实现着大数据的价值。 所以本着学习的态度&#xff0…

四、设置主机名和域名映射

目录 1、配置每台虚拟机主机名 2、配置每台虚拟机域名映射 1、配置每台虚拟机主机名

MATLAB实战 | S函数的设计与应用

S函数用于开发新的Simulink通用功能模块&#xff0c;是一种对模块库进行扩展的工具。S函数可以采用MATLAB语言、C、C、FORTRAN、Ada等语言编写。在S函数中使用文本方式输入公式、方程&#xff0c;非常适合复杂动态系统的数学描述&#xff0c;并且在仿真过程中可以对仿真进行更精…

ASP.NET版本WOL服务的使用

本文以WOL为例&#xff0c;演示如何通过 GPT-4 让其为 WebAPI 项目设计一个网页。其中介绍了如何让GPT4生成相关功能&#xff0c;添加动画效果&#xff0c;接口鉴权等。 1. 背景 前面我们已经完成了一个WOL服务的开发&#xff0c;并将其迁移改造为了 ASP.NET 服务并完成了部署…

02数仓平台Zookeeper

概述 ZooKeeper是一种分布式协调服务&#xff0c;用于管理大型主机集。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper通过其简单的架构和API解决了这个问题。ZooKeeper允许开发人员专注于核心应用程序逻辑&#xff0c;而不必担心应用程序的分布式性质。 Zookeepe…