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

news2025/1/31 11:14:23

在这里插入图片描述

阅读导航

  • 引言
  • 一、 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.coloradmin.cn/o/1278979.html

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

相关文章

elasticsearch 内网下如何以离线的方式上传任意的huggingFace上的NLP模型(国内闭坑指南)

es自2020年的8.x版本以来&#xff0c;就提供了机器学习的能力。我们可以使用es官方提供的工具eland&#xff0c;将hugging face上的NLP模型&#xff0c;上传到es集群中。利用es的机器学习模块&#xff0c;来运维部署管理模型。配合es的管道处理&#xff0c;来更加便捷的处理数据…

深度学习记录--logistic回归损失函数向量化实现

前言 再次明确向量化的目的&#xff1a;减少for循环的使用&#xff0c;以更少的代码量和更快的速度来实现程序 正向传播的向量化 对于,用向量化来实现&#xff0c;只需要就可以完成&#xff0c;其中,, ps.这里b只是一个常数&#xff0c;但是依然可以加在每个向量里(python的…

TLS协议握手流程

浅析 TLS&#xff08;ECDHE&#xff09;协议的握手流程&#xff08;图解&#xff09; - 知乎 前言 通过 wireshark 抓取 HTTPS 包&#xff0c;理解 TLS 1.2 安全通信协议的握手流程。 重点理解几个点&#xff1a; TLS 握手流程&#xff1a;通过 wireshark 抓取 HTTPS 包理解…

项目中遇到的半导体公司

作为一个技术人&#xff0c;我并不是亲美&#xff0c;从技术的实事求是角度讲&#xff0c;不得不感叹欧美的半导体技术。他们的datasheet能学到的东西太多太多&#xff1b;我甚至佩服他们缜密的逻辑。从他们的文章中领悟我们技术到底有多low&#xff0c;没办法一个一个了解所有…

Nginx转发内网Flv视频流

1、环境说明 Docker Nginx&#xff1a;1.21.5 实现Nginx ssl转发内网flv视频流 2、配置nginx.conf http {upstream live {server 10.10.10.10:8300;keepalive 64;}map $http_upgrade $connection_upgrade {default upgrade; close;}server {listen 80;listen 443…

ssm+vue的罪犯信息管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的罪犯信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

Redis hash表源码解析

整体数据结构&#xff1a;链式hash解决hash冲突、采用渐进式hash来完成扩容过程。 /** 哈希表节点*/ typedef struct dictEntry {// 键void *key;// 值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点&#xff0c;形成链表struct dictEntry *next;} dict…

Adobe Acrobat DC 将PDF转曲步骤

1、编辑--更多--背景--添加 2、只需要将不透明度调为0即可。 3、工具--印刷制作 4、拼合器预览 5、只需要将下面标出来的地方勾选即可 6、可以另存为&#xff0c;不影响源文件 7、检查是否成功&#xff0c;文件--属性--字体为空&#xff0c;说明成功了 参考资料&#xff1a; …

详解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…

【论文阅读】ICRA: An Intelligent Clustering Routing Approach for UAV Ad Hoc Networks

文章目录 论文基本信息摘要1.引言2.相关工作3.PROPOSED SCHEME4.实验和讨论5.总结补充 论文基本信息 《ICRA: An Intelligent Clustering Routing Approach for UAV Ad Hoc Networks》 《ICRA:无人机自组织网络的智能聚类路由方法》 Published in: IEEE Transactions on Inte…

FISCO-BCOS 在ARM系统架构搭建节点(国密版)

问题&#xff1a; 使用 fisco-bcos v2.9.1 搭建一个节点&#xff0c;批量上链1000条数据&#xff0c;在上链200条-400条数据之间节点会出现异常&#xff0c;导致后面数据不能上链。 系统环境 操作系统&#xff1a;统信 查看系统构架 ld -version rootuos-PC:/# ld -version …

RK3568平台开发系列讲解(Linux系统篇)netlink 监听广播信息

** 🚀返回专栏总目录 文章目录 一、什么是netlink 机制二、netlink 的使用2.1、创建 socket2.2、绑定套接字2.3、接收数据沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍如何通过 netlink 监听广播信息。 一、什么是netlink 机制 Netlink 是 Linux 内核中…

【C/C++笔试练习】公有派生、构造函数内不执行多态、抽象类和纯虚函数、多态中的缺省值、虚函数的描述、纯虚函数的声明、查找输入整数二进制中1的个数、手套

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;公有派生&#xff08;2&#xff09;构造函数内不执行多态&#xff08;3&#xff09;抽象类和纯虚函数&#xff08;4&#xff09;多态中的缺省值&#xff08;5&#xff09;程序分析&#xff08;6&#xff09;重载和隐藏&a…

使用gcloud SDK 管理和部署 Cloud run service

查看cloud run 上的service 列表&#xff1a; gcloud run services list > gcloud run services listSERVICE REGION URL LAST DEPLOYED BY LAST DEPL…

【Go语言反射reflect】

Go语言反射reflect 一、引入 先看官方Doc中Rob Pike给出的关于反射的定义&#xff1a; Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of …

Android BT HCI分析简介

对于蓝牙开发者来说&#xff0c;通过HCI log可以帮助我们更好地分析问题&#xff0c;理解蓝牙协议&#xff0c;就好像网络开发一定要会使用Wireshark分析网络协议一样。 本篇主要介绍HCI log的作用、如何抓取一份HCI log&#xff0c;并结合一个实际的例子来说明如何分析HCI log…

004、简单页面-基础组件

之——基础组件 目录 之——基础组件 杂谈 正文 1.Image 1.0 数据源 1.1 缩放 1.2 大小 1.3 网络图片 2.Text 2.0 数据源 2.1 大小 2.2 粗细 2.3 颜色 2.5 样式字体 2.6 基础示例 2.7 对齐 2.8 省略 2.9 划线 3.TextInput 3.1 输入类型 3.2 提示文…

基于Springboot + vue的汽车资讯网站

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

Leetcode—392.判断子序列【简单】

2023每日刷题&#xff08;四十七&#xff09; Leetcode—392.判断子序列 双指针实现代码 bool isSubsequence(char* s, char* t) {int lens strlen(s);int lent strlen(t);int left 0, right 0;if(lens 0) {return true;}while(right < lent) {if(t[right] s[left])…

Javaweb之Vue组件库Element案例分页工具栏的详细解析

4.4.3.5.3 分页工具栏 分页条我们之前做过&#xff0c;所以我们直接找到之前的案例&#xff0c;复制即可&#xff0c;代码如下&#xff1a; 其中template模块代码如下&#xff1a; <!-- Pagination分页 --> <el-paginationsize-change"handleSizeChange"c…