线程(thread)

目录

线程的基本特性

pthread库的主要函数

pthread_create

pthread_join

pthread_exit

pthread_mutex_init

pthread_mutex_lock 和 pthread_mutex_unlock

pthread_cond_init

pthread_cond_wait 和 pthread_cond_signal / pthread_cond_broadcast

pthread_cond_destroy

pthread_self

C++11提供的std::thread类

构造函数

 成员函数

示例

c和c++ 线程创建差异 使用场景推荐

C 语言线程创建

C++ 语言线程创建

使用场景推荐

主线程退出,子线程会退出吗?

示例

示例

某个线程异常,会导致整个进程异常吗?

top -H和pstack

top -H

pstack


基本概念

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix/Linux系统中,使用POSIX线程库(也称为pthread库)来支持多线程编程。

线程的基本特性

  1. 独立性:线程可以独立执行自己的线程体,它与其他线程共享进程的内存和其他资源。
  2. 并发性:线程是进程内的一条执行路径,因此多个线程可以在单核CPU上并发执行,通过CPU时间片轮转的方式实现。
  3. 共享性:线程共享进程的资源,包括内存空间、打开的文件描述符等。

pthread库的主要函数

  1. pthread_create:创建新线程。
  2. pthread_exit:终止调用它的线程。
  3. pthread_join:等待指定的线程结束。
  4. pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock, pthread_mutex_destroy:用于互斥锁的操作,实现线程同步。
  5. pthread_cond_init, pthread_cond_wait, pthread_cond_signal, pthread_cond_destroy:条件变量相关操作,用于线程间的协调。
  6. pthread_self:获取线程ID

pthread_create

功能:创建一个新线程。

原型

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

参数

  • pthread_t *thread:指向线程标识符的指针,用来获取新创建线程的ID。
  • const pthread_attr_t *attr:线程属性,通常为NULL,表示使用默认属性。
  • void *(*start_routine) (void *):线程函数指针,线程启动后执行的函数。
  • void *arg:传递给线程函数的参数。

示例

#include <stdio.h>  
#include <pthread.h>  
  
void *print_hello(void *threadid) {  
    long tid;  
    tid = (long)threadid;  
    printf("Hello World! It's me, thread #%ld!\n", tid);  
    pthread_exit(NULL);  
}  
  
int main (int argc, char *argv[]) {  
    pthread_t threads[3];  
    int rc;  
    long t;  
    for(t = 0; t < 3; t++){  
        printf("In main: creating thread %ld\n", t);  
        rc = pthread_create(&threads[t], NULL, print_hello, (void *)t);  
        if (rc){  
            printf("ERROR; return code from pthread_create() is %d\n", rc);  
            exit(-1);  
        }  
    }  
    // 等待所有线程完成  
    for(t = 0; t < 3; t++) {  
        pthread_join(threads[t], NULL);  
    }  
    pthread_exit(NULL);  
}

pthread_join

功能:等待一个特定的线程终止。

原型

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

参数

  • pthread_t thread:要等待的线程的ID。
  • void **retval:指向一个指针的指针,用来获取线程的返回值,如果不需要可以设为NULL。

示例:在上面的main函数中已经展示了如何使用pthread_join来等待线程完成。

pthread_exit

功能:终止调用它的线程。

原型

void pthread_exit(void *retval);

参数

  • void *retval:线程的返回值,可以被其他线程通过pthread_join获取。

示例:在print_hello函数中使用了pthread_exit(NULL);来终止线程。

pthread_mutex_init

功能:初始化互斥锁。

原型

int pthread_mutex_init(pthread_mutex_t *restrict mutex,  
                        const pthread_mutexattr_t *restrict attr);

参数

  • pthread_mutex_t *restrict mutex:指向互斥锁的指针。
  • const pthread_mutexattr_t *restrict attr:互斥锁属性,通常为NULL。

pthread_mutex_lock 和 pthread_mutex_unlock

功能:分别用于锁定和解锁互斥锁。

原型

int pthread_mutex_lock(pthread_mutex_t *mutex);  
int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数

  • pthread_mutex_t *mutex:指向互斥锁的指针。
  • 示例
pthread_mutex_t mutex;  
pthread_mutex_init(&mutex, NULL);  
  
// 锁定互斥锁  
pthread_mutex_lock(&mutex);  
  
// 临界区代码...  
  
// 解锁互斥锁  
pthread_mutex_unlock(&mutex);  
  
pthread_mutex_destroy(&mutex);

pthread_cond_init

功能:初始化条件变量。

原型

int pthread_cond_init(pthread_cond_t *restrict cond,  
                       const pthread_condattr_t *restrict attr);

参数

  • pthread_cond_t *restrict cond:指向条件变量的指针。
  • const pthread_condattr_t *restrict attr:条件变量属性,通常为NULL。

pthread_cond_wait 和 pthread_cond_signal / pthread_cond_broadcast

功能pthread_cond_wait用于等待条件变量被信号触发,pthread_cond_signal用于唤醒等待该条件变量的一个线程,pthread_cond_broadcast用于唤醒等待该条件变量的所有线程。

原型

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);  
int pthread_cond_signal(pthread_cond_t *cond);  
int pthread_cond_broadcast(pthread_cond_t *cond);

参数

  • pthread_cond_t *cond:指向条件变量的指针。
  • pthread_mutex_t *mutex:在pthread_cond_wait中,指向互斥锁的指针,该互斥锁必须在调用pthread_cond_wait之前被锁定,并且在等待期间会被自动解锁,在条件变量被信号触发并返回时会被重新锁定。

pthread_cond_destroy

功能:销毁条件变量。

原型

int pthread_cond_destroy(pthread_cond_t *cond);

参数

  • pthread_cond_t *cond:指向要销毁的条件变量的指针。

示例(条件变量和互斥锁结合使用):

#include <stdio.h>  
#include <pthread.h>  
  
pthread_mutex_t mutex;  
pthread_cond_t cond;  
int ready = 0;  
  
void *thread_function(void *arg) {  
    pthread_mutex_lock(&mutex);  
    while (!ready) {  
        pthread_cond_wait(&cond, &mutex);  
    }  
    printf("Thread is ready to work!\n");  
    pthread_mutex_unlock(&mutex);  
    pthread_exit(NULL);  
}  
  
int main() {  
    pthread_t thread;  
  
    pthread_mutex_init(&mutex, NULL);  
    pthread_cond_init(&cond, NULL);  
  
    pthread_create(&thread, NULL, thread_function, NULL);  
  
    // 模拟一些准备工作  
    sleep(1);  
    pthread_mutex_lock(&mutex);  
    ready = 1;  
    pthread_cond_signal(&cond); // 唤醒等待的线程  
    pthread_mutex_unlock(&mutex);  
  
    pthread_join(thread, NULL);  
  
    pthread_mutex_destroy(&mutex);  
    pthread_cond_destroy(&cond);  
  
    pthread_exit(NULL);  
}

在这个示例中,我们创建了一个线程thread_function,它会在条件变量cond上等待,直到主线程通过pthread_cond_signal发送信号。主线程在发送信号前会先锁定互斥锁mutex,更改ready变量的值,然后发送信号并解锁互斥锁。这样,等待的线程在收到信号后会继续执行。

pthread_self

功能:pthread_self 是一个 POSIX 线程(pthreads)库中的函数,用于获取当前线程的线程标识符(thread identifier)。线程标识符是一个非零的无符号整数,它在其进程中唯一地标识一个线程。

原型:

pthread_t pthread_self(void);

参数

该函数没有参数。

返回值

pthread_self 返回一个 pthread_t 类型的值,表示调用线程的线程标识符。这个值在调用线程的生命周期内是唯一的,并且在进程中的所有线程中都是不同的。

使用场景

pthread_self 在多线程编程中非常有用,尤其是在需要区分不同线程或跟踪线程状态的场景中。例如,你可能在一个线程中存储了一些与线程相关的数据,并希望用线程标识符作为键来访问这些数据。

示例

下面是一个简单的示例,展示了如何使用 pthread_self

#include <stdio.h>  
#include <pthread.h>  
  
void *thread_function(void *arg) {  
    pthread_t thread_id = pthread_self();  
    printf("Thread ID: %lu\n", (unsigned long)thread_id);  
    return NULL;  
}  
  
int main() {  
    pthread_t thread1, thread2;  
    int rc1, rc2;  
  
    // 创建两个线程  
    rc1 = pthread_create(&thread1, NULL, thread_function, NULL);  
    rc2 = pthread_create(&thread2, NULL, thread_function, NULL);  
  
    if (rc1 || rc2) {  
        printf("Error: return code from pthread_create() is %d\n", rc1);  
        return 1;  
    }  
  
    // 等待线程结束  
    pthread_join(thread1, NULL);  
    pthread_join(thread2, NULL);  
  
    return 0;  
}

在这个示例中,我们创建了两个线程,并在每个线程中调用 pthread_self 来获取并打印其线程标识符。这样,我们就可以看到每个线程都有一个唯一的标识符。

Linux下的线程操作函数提供了创建、等待、退出线程以及互斥锁和条件变量的基本功能。正确理解和使用这些函数是编写高效且安全的多线程程序的关键。每个函数都有其特定的用途和参数要求,程序员应该根据具体需求来选择合适的函数,并确保正确使用。同时,还需要注意线程同步和互斥的问题,以避免出现竞态条件和其他线程相关的问题。

C++11提供的std::thread类

std::thread 是 C++11 标准库中提供的一个类,用于表示和管理线程。它简化了线程创建和管理的复杂性,使得开发者可以更加容易地编写多线程程序。

构造函数

std::thread 类有多个构造函数,用于创建线程。

  • 默认构造函数

std::thread() noexcept;

创建一个空的线程对象,表示没有关联的线程。

  • 初始化构造函数

std::thread( thread_function, Args&&... args );

创建一个新线程,该线程将执行 thread_function 函数,并传递 args 作为参数。

 成员函数

  • join

void join();

阻塞当前线程,直到调用 join 的线程完成执行。

  • detach

void detach();

将线程分离,允许它独立运行。一旦线程被分离,就不能再调用 join

  • swap

void swap( thread& other ) noexcept;

交换两个线程对象。

  • get_id

thread::id get_id() const noexcept;

返回线程的 ID。

  • hardware_concurrency(静态成员函数):

static unsigned int hardware_concurrency() noexcept;

返回可用的硬件并发线程数。

  • native_handle

native_handle_type native_handle();

返回与 std::thread 对象关联的底层实现的句柄。

  • is_joinable

bool is_joinable() const noexcept;

如果线程可以调用 join,则返回 true

示例

下面是一个简单的 std::thread 使用示例:

#include <iostream>  
#include <thread>  
#include <chrono>  
  
void thread_function(int n) {  
    for (int i = 0; i < n; ++i) {  
        std::cout << "Thread function is running..." << std::endl;  
        std::this_thread::sleep_for(std::chrono::seconds(1));  
    }  
}  
  
int main() {  
    std::thread t(thread_function, 5);  // 创建新线程,执行 thread_function 函数,参数为 5  
  
    std::cout << "Main thread is running..." << std::endl;  
    std::this_thread::sleep_for(std::chrono::seconds(3));  
  
    t.join();  // 等待新线程完成  
  
    return 0;  
}

在这个例子中,我们创建了一个新线程 t,它执行 thread_function 函数,并传递参数 5。主线程和新线程会同时运行,但主线程会在 3 秒后通过调用 t.join() 等待新线程完成。这样,我们确保了主线程不会在新线程完成之前退出。

c和c++ 线程创建差异 使用场景推荐

C 和 C++ 在线程创建方面存在一些差异,这主要源于它们各自的标准库和编程模型的不同。以下是对这两个语言在线程创建方面的比较以及使用场景推荐:

C 语言线程创建

在 C 语言中,创建线程通常依赖于操作系统提供的线程 API 或第三方库,如 POSIX 线程(pthreads)。pthreads 是一套在 Unix-like 系统(包括 Linux 和 macOS)上广泛使用的线程库。

C++ 语言线程创建

C++11 标准引入了 <thread> 头文件,它提供了一个高级别的、跨平台的线程库。这使得在 C++ 中创建和管理线程变得相对简单和直接。

使用场景推荐

C 语言:

  • 当你需要编写跨平台代码,并且目标平台可能不支持 C++11 或更高版本的特性时,C 语言和 pthreads 是一个不错的选择。
  • 如果你正在维护一个现有的 C 语言项目,并且需要添加多线程功能,使用 pthreads 或其他类似的库可以保持代码的一致性。
  • 对于性能敏感的应用,C 语言和底层的线程 API 可能会提供更细粒度的控制和更高的性能。

C++ 语言:

  • 当你可以使用 C++11 或更高版本时,使用 std::thread 通常更为方便和直观。它提供了更高级别的抽象,减少了与操作系统底层 API 的直接交互。
  • std::thread 与 C++ 的其他特性(如 RAII、异常处理等)结合得更好,使得编写健壮的多线程代码更为容易。
  • 在编写新的 C++ 项目或重构旧项目时,如果需要使用多线程,优先考虑使用 std::thread
  • 使用这个类可以将任意签名形式的函数作为线程函数

示例

#include<thread>
#include<iostream>
void fun1()
{
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout<<__func__<<" running"<<std::endl;
    }
}
void fun2(int a)
{
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        std::cout<<__func__<<" running param = "<<a<<std::endl;
    }
}
int main()
{
    std::thread t1(fun1);
    std::thread t2(fun2,100);
    t1.join();
    t2.join();
    return 0;
}

 编译运行

  • std::thread 对象在线程函数运行期间必须是有效的

示例

#include<thread>
#include<iostream>
void fun1()
{
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout<<__func__<<" running"<<std::endl;
    }
}
void fun2()
{
    std::thread t1(fun1);
}
int main()
{
    fun2();
    while(1);
    return 0;
}

编译运行

崩溃的原因是,在fun2函数调用结束后,fun2中的局部变量 t1(线程对象)被销毁,而此时线程函数仍在运行。所以在使用 std::thread类时,必须保证在线程函数运行期间其线程对象有效。std::thread对象提供了一个 detach方法,通过这个方法可以让线程对象与线程函数脱离关系,这样即使线程对象被销毁,也不影响线程函数的运行。我们只需在func函数中调用detach方法即可
 

修改如下

#include<thread>
#include<iostream>
void fun1()
{
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout<<__func__<<" running"<<std::endl;
    }
}
void fun2()
{
    std::thread t1(fun1);
    t1.detach();
}
int main()
{
    fun2();
    while(1);
    return 0;
}

编译运行

总的来说,选择使用 C 还是 C++ 来创建线程取决于你的具体需求、项目约束以及你对这两个语言的熟悉程度。在可能的情况下,使用 C++ 的 std::thread 通常是一个更现代、更便捷的选择。

主线程退出,子线程会退出吗?

在Linux系统中,主线程的退出方式决定了子线程是否也会随之退出。如果主线程以return的方式退出,实际上会调用exit()函数,这个函数会执行关闭IO操作以及关闭其他子线程的操作,因此子线程会随之退出。

示例

#include<pthread.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

void* test1(void *)
{
    while(1)
    {   
        usleep(300000);
        printf("test1 \n"); 
    }
}
void* test2(void *)
{
    while(1)
    {   
        usleep(300000);
        printf("test2 \n"); 
    }
}
int main()
{
    
    pthread_t pth1;
    pthread_t pth2;

    int iRet1 = pthread_create(&pth1,nullptr,test1,nullptr);
    int iRet2 =  pthread_create(&pth2,nullptr,test2,nullptr);
    // pthread_join(pth1,nullptr);
    // pthread_join(pth2,nullptr);

    printf("Main: program completed. Exiting.\n");  
    return 0;
    // pthread_exit(NULL);  
}

编译执行

然而,如果主线程以pthread_exit()函数的方式退出,实际上是提前结束了主线程,此时并不会执行后续的exit()函数。因此,这种方式可以达到主线程退出而子线程继续运行的目的。

示例

#include<pthread.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

void* test1(void *)
{
    while(1)
    {   
        usleep(300000);
        printf("test1 \n"); 
    }
}
void* test2(void *)
{
    while(1)
    {   
        usleep(300000);
        printf("test2 \n"); 
    }
}
int main()
{
    
    pthread_t pth1;
    pthread_t pth2;

    int iRet1 = pthread_create(&pth1,nullptr,test1,nullptr);
    int iRet2 =  pthread_create(&pth2,nullptr,test2,nullptr);
    // pthread_join(pth1,nullptr);
    // pthread_join(pth2,nullptr);

    printf("Main: program completed. Exiting.\n");  
    pthread_exit(NULL);  
    return 0;
    // pthread_exit(NULL);  
}

这种情况也会导致进程变成僵尸进程。这是一种不好的做法,在实际开发中应该避免产生僵尸进程

总的来说,在Linux系统中,主线程和子线程之间没有必然的退出次序关系。主线程退出,子线程可以继续执行;子线程退出,主线程也可以继续执行。但值得注意的是,如果进程终止,那么进程下所有的线程都会终止。

此外,需要注意的是,如果主线程退出而没有等待其他子线程执行完毕,可能会导致进程变成僵尸进程。僵尸进程是应该被避免的,因为它们会占用系统资源。因此,在实际编程中,应该确保主线程在退出之前等待其他子线程执行完毕,可以通过调用pthread_exit()等方法来实现。

综上所述,主线程退出时子线程是否会退出,取决于主线程的退出方式以及是否采取了适当的措施来避免产生僵尸进程。

某个线程异常,会导致整个进程异常吗?

在Linux中,一个线程的异常通常不会直接导致整个进程异常。每个线程都有自己的执行上下文和堆栈,因此一个线程的异常通常只会影响到该线程本身,而不会影响到其他线程或整个进程的稳定性。

然而,存在一些情况下,一个线程的异常可能会影响到整个进程的正常运行,例如:

  1. 未捕获的异常:如果一个线程中的异常没有被正确捕获处理,那么这个异常可能会导致整个进程异常终止。这通常发生在没有适当的异常处理机制的情况下,例如没有使用 try-catch 块捕获异常。

  2. 资源泄漏:线程异常可能会导致资源泄漏,如果这些资源没有被正确释放,可能会影响到整个进程的性能或稳定性。

  3. 竞态条件:线程异常可能会导致竞态条件或其他线程安全问题,这可能会导致进程的不确定行为或崩溃。

虽然一个线程的异常通常不会直接导致整个进程的异常,但是它可能会间接地影响到进程的稳定性。因此,确保线程中的异常能够被正确地捕获和处理是保障整个进程稳定性的重要一环。

top -H和pstack

top -H 和 pstack 是两个在 Linux 系统中用于诊断和分析多线程进程的工具。下面我会为你详细解释这两个命令的用途和工作原理。

top -H

top 命令是一个常用的性能分析工具,它可以实时显示系统中各个进程的资源占用状况,类似于 Windows 的任务管理器。默认情况下,top 显示的是各个进程的总体信息,而不会区分进程中的各个线程。

-H 选项是 top 的一个参数,用于使 top 以线程模式显示信息。当使用这个选项时,top 会列出每个进程的所有线程,并显示每个线程的 CPU 使用率、内存使用情况等信息。这对于分析多线程应用程序的性能问题非常有用。

使用 top -H,你可以观察各个线程的资源占用情况,从而找出可能的性能瓶颈或问题所在。

pstack

pstack 是一个用于显示进程栈跟踪信息的工具。它可以打印出指定进程的调用栈信息,从而帮助你分析进程在某一时刻的执行状态。

当你怀疑一个进程可能陷入了死循环或者出现了其他异常行为时,pstack 可以帮助你查看该进程当前正在执行的代码路径。通过比较不同时间点的栈跟踪信息,你可以了解进程的执行流程,并找出可能的问题所在。

使用 pstack 时,你需要提供进程的 PID(进程 ID)作为参数。例如,pstack 12345 将显示 PID 为 12345 的进程的栈跟踪信息。

这两个工具在 Linux 系统的性能分析和调试中非常有用,特别是在处理多线程应用程序时。它们可以帮助你深入了解进程和线程的执行状态,从而找出并解决潜在的问题。

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

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

相关文章

探索Linux世界:基本指令(文件查看、时间相关、grep、打包压缩及相关知识)

今天继续介绍一些指令 文章目录 1.cat - 查看文件1.1输出重定向和追加重定向1.2指令echo 2.more 指令3.less - 逐页查看文本文件内容4.head- 显示文件开头部分内容5.tail - 显示文件末尾部分内容5.1输入重定向&#xff08;<&#xff09;5.2管道&#xff08;|&#xff09; 6.…

【C++从练气到飞升】03---构造函数和析构函数

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 ⛳️推荐 一、类的6个默认成员函数 二、构造函数 1. 构造函数的概念 2. 构造函数的定义 3. 构造函数的特性 三、析构函…

Python环境下一维时间序列的高斯均值滤波分解方法

信号分解是一种可以将复杂的观测信号分解为若干子信号的时频分析技术。该技术可以通过分解得到的子信号来有效表征观测信号内部的时频特性&#xff0c;进而能够从观测信号中提取出有用信息。因此&#xff0c;信号分解在信号处理领域中发挥着重要的作用。 傅里叶分解是早期常用…

论文学习——基于枢轴点预测和多样性策略混合的动态多目标优化

论文题目&#xff1a;A dynamic multi-objective optimization based on a hybrid of pivot points prediction and diversity strategies 基于枢轴点预测和多样性策略混合的动态多目标优化&#xff08;Jinhua Zheng a,b,d, Fei Zhou a,b,∗, Juan Zou a,b, Shengxiang Yang a…

mysql数据库(下)

目录 约束 约束的概念和分类 1、约束的概念&#xff1a; 2、约束的分类 1、主键约束 2、默认约束 3、非空约束 4、唯一约束 5、外键约束 约束 约束的概念和分类 1、约束的概念&#xff1a; 约束时作用于表中列上的规则&#xff0c;用于限制加入表的数据约束的存在保证…

Java学习笔记------常用API(二)

Object 无有参构造 public Object() 空参构造 成员方法&#xff1a; public String toString() 返回对象的字符串表示 public boolean equals(object obj) 比较两个对象是否相等 Object默认用号比较地址值&#xff0c;需要重写才能比较属性值 protected O…

知识文档管理系统平台:企业管理的王炸

无论是企业内部的文件共享&#xff0c;还是团队之间的协作编辑&#xff0c;知识文档管理系统都能发挥巨大的作用。它帮助企业整理、存储和查找各种文档资料&#xff0c;这不仅能提高企业的工作效率&#xff0c;还能增强企业的竞争力。今天就跟着LookLook同学一起来深入了解知识…

4款好用的AI写作软件推荐,让你成为写作大神

写作已经成为我们日常生活和工作中必不可少的一部分&#xff0c;当我们在还绞尽脑汁的去想如何写作的时候&#xff0c;别人已经弯道超车用上了人工智能写作软件&#xff0c;今天&#xff0c;小编想为大家推荐4款好用的AI写作软件&#xff0c;让你在几秒钟内生成高质量文章的同时…

ADGUARD规则备份

ADGUARD规则备份 文章目录 ADGUARD规则备份使用方法规则 使用方法 规则 123pan.com###app > div.ant-spin-nested-loading.global-loading > div.ant-spin-container > div > div.appdiv.web-wrap > div.banner-container-pc:nth-child(3) bilibili.com###comm…

【Typescript】any和unknown的区别

any----没有任何约束【JavaScript中基本都是 any类型&#xff0c;可随意赋值】 unknow----可以接受任何类型的赋值&#xff0c;但不可以赋值给其他任意类型【除非使用as断言转换】 let o:number[] [1,2,3]// 可接收任何类型 let a:unknown o// 不可传递给其他类型 let x:st…

基于SpringBoot的“实习管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“实习管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 学生注册界面图 后台登录界面图 …

本地mysql5.7以上版本配置及my.ini

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

当页面滚动到指定位置时,底部按钮出现并固定到底部——js基础积累

需求场景&#xff1a; 当页面滚动到指定位置时&#xff0c;底部按钮出现并固定到底部。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><metaname"viewport"content"widthdevice-width, user…

Jetpack Compose 动画正式开始学习

1. 简单值动画 //将一个Color简单值 从一个值 变化到另一个 另一个简单值 就用 animateColorAsStateval backgroundColor by animateColorAsState(if (tabPage TabPage.Home) Purple100 else Green300) 动画其实就是 一个状态不停发生改变导致 组件不断重组产生的过程 2.…

iPhone, Android 手机是如何收到推送通知的?

本文转自 公众号 ByteByteGo&#xff0c;如有侵权&#xff0c;请联系&#xff0c;立即删除 iPhone, Android 手机是如何收到推送通知的&#xff1f; 我们的手机或电脑是如何收到推送通知的&#xff1f; 通常我们可以使用消息解决方案 Firebase 来支持通知推送。下图显示了 Fi…

刷题DAY21 | LeetCode 530-二叉搜索树的最小绝对差 501-二叉搜索树中的众数 236-二叉树的最近公共祖先

530 二叉搜索树的最小绝对差&#xff08;easy&#xff09; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 思路&#xff1a;双指针法 代码实现&#xff1a; class So…

ArmSoM Rockchip系列产品 通用教程 之 RTC 使用

1. RTC 简介​ RTC&#xff1a;(Real_Time Clock)&#xff1a;实时时钟 HYM8563是一种低功耗实时时钟&#xff08;RTC&#xff09;芯片&#xff0c;用于提供精确的时间和日期信息。它提供一个可编程的时钟输出&#xff0c;一个中断输出和一个掉电检测器&#xff0c;所有的地址…

从零开始,一步步构建服务网格istio

一、环境情况 环境&#xff1a;Ubuntu20.04 机器数量&#xff1a;单机1台 IP&#xff1a;10.9.2.83 二、准备知识 为什么使用 Istio&#xff1f; Istio提供了一种更高级别的服务网格解决方案&#xff0c;它可以简化和加强 Kubernetes 集群中的服务间通信、流量管理、安全…

SpringBoot配置加载顺序和SpringBoot分离打包:将jar包与lib依赖、配置文件分开

文章目录 一、SpringBoot配置加载顺序1.SpringBoot配置优先级&#xff08;1&#xff09;命令行参数&#xff08;2&#xff09;配置文件 二、SpringBoot分离打包&#xff1a;将jar包与lib依赖、配置文件分开1.pom文件配置2.打包后的目录结构 一、SpringBoot配置加载顺序 官方文…

下载文件,无法获取header中的Content-Disposition

问题&#xff1a;axios跨域请求时&#xff0c;无法获取header中的Content-Disposition&#xff0c;并且network中已显示Content-Disposition 在使用CORS方式跨域时&#xff0c;浏览器只会返回默认的头部Header 解决&#xff1a; 后端在返回时&#xff0c;需要设置公开的响应…