【C++】线程库

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:C++

在这里插入图片描述


前言

C++11标准引入了线程库,通过其可以在C++程序中方便地创建和管理线程。以下是一些常用的线程库组件:

  1. std::threadstd::thread类用于表示一个线程,通过其构造函数可以传入一个可调用对象(函数或者lambda表达式)作为线程的入口点,然后调用join()detach()来等待线程结束或者将线程分离。

  2. std::mutexstd::mutex类用于实现互斥锁防止多个线程同时访问共享资源导致数据竞争。可以通过std::lock_guardstd::unique_lock来管理std::mutex的锁定和解锁。

  3. std::condition_variablestd::condition_variable类用于线程间的条件变量通信,一个线程可以等待另一个线程满足特定条件后再继续执行。

  4. std::atomicstd::atomic模板类提供了原子操作,确保在多线程环境下对共享变量的操作是原子的,避免竞态条件。

  5. std::future 和 std::promisestd::futurestd::promise用于在线程间传递异步操作的结果。std::promise用于设置值,而std::future用于获取这个值。

这些组件提供了丰富的功能,使得在C++程序中使用多线程变得更加容易和安全。


目录

  • 👉🏻thread
    • 成员函数
      • join() 函数:
      • joinable()函数
      • detach函数
      • get_id()函数
    • 静态成员函数
      • std::this_thread::yield()函数
      • std::this_thread::sleep_for()函数
      • std::this_thread::sleep_until()函数
    • 为什么thread的传参里面如果想传引用一定要用std::ref?
  • 👉🏻mutex
    • 基本接口函数
    • lock_guard和unique_lock
  • 👉🏻condition_variable

👉🏻thread

std::thread 是 C++11 标准库中用于创建和管理线程的类。下面是 std::thread 类中一些重要的接口函数:

  1. 构造函数

    • explicit thread(Args&&... args);:接受线程函数参数,并在构造函数中启动新线程。
  2. 成员函数

    • void join();:等待线程结束,阻塞当前线程直到被调用的线程执行完毕。
    • bool joinable() const noexcept;:检查线程是否可加入(joinable),即线程对象是否与实际的线程相关联。
    • void detach();:分离线程,允许线程在后台继续执行,线程结束时自动释放资源。
    • std::thread::id get_id() const noexcept;:获取线程的唯一标识符。
    • static unsigned int hardware_concurrency() noexcept;:获取当前系统支持的并发线程数。
  3. 静态成员函数

    • static void yield();:提示调度器放弃当前时间片,允许其他线程执行。
    • static void sleep_for(const std::chrono::duration& rel_time);:使当前线程休眠一段时间。
    • static void sleep_until(const std::chrono::time_point& abs_time);:使当前线程休眠直到指定的时间点。

这些是 std::thread 类中最常用的接口函数,可以帮助你创建、管理和操作线程。当使用 std::thread 时,确保理解这些函数的作用和用法,以便有效地编写多线程程序。

成员函数

join() 函数:

  • 函数原型

    void join();
    
  • 参数意义join() 函数没有参数。

  • 返回值void

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        std::cout << "Thread running\n";
    }
    
    int main() {
        std::thread t(threadFunction);
        t.join(); // 等待线程执行完毕
        std::cout << "Main thread\n";
        return 0;
    }
    

    在上面的示例中,join() 函数会阻塞主线程,直到线程 t 执行完毕后才继续执行主线程。

joinable()函数

  • 函数原型

    bool joinable() const noexcept;
    
  • 参数意义joinable() 函数没有参数。

  • 返回值bool,如果线程对象与实际线程相关联,则返回 true;否则返回 false

  • 示例代码

    #include <iostream>
    #include <thread>
    
    int main() {
        std::thread t;
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        t = std::thread([](){
            std::cout << "Thread running\n";
        });
        
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        t.join();
        std::cout << "Is thread joinable? " << (t.joinable() ? "Yes" : "No") << std::endl;
        
        return 0;
    }
    

    在这个示例中,先输出 No,表示初始时线程 t 不可加入;然后创建线程并输出 Yes;最后再次输出 No,表示线程在调用 join() 后不可再次加入。

detach函数

  • 函数原型

    void detach();
    
  • 参数意义detach() 函数没有参数。

  • 返回值void

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        std::cout << "Thread running\n";
    }
    
    int main() {
        std::thread t(threadFunction);
        t.detach(); // 分离线程
        // 注意:分离后不能再调用join(),否则会导致程序终止
        
        std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待子线程执行完毕(非最佳做法)
        
        std::cout << "Main thread\n";
        return 0;
    }
    

    在这个示例中,线程 t 被分离后,主线程不再等待其执行完毕,而是继续执行后续代码。

get_id()函数

  • 函数原型

    std::thread::id get_id() const noexcept;
    
  • 参数意义get_id() 函数没有参数。

  • 返回值std::thread::id,表示线程的唯一标识符。

  • 示例代码

    #include <iostream>
    #include <thread>
    
    int main() {
        std::thread t([]{
            std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
        });
        
        std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
        
        t.join();
        return 0;
    }
    

    这个示例演示了如何使用 get_id() 函数获取线程的唯一标识符,并输出主线程和子线程的 ID。

静态成员函数

std::this_thread::yield()函数

  • 函数原型

    void yield();
    
  • 参数意义yield() 函数没有参数。

  • 返回值void

  • 功能yield() 函数会将当前线程放弃处理器,以便其他线程有机会执行。调用此函数会暗示操作系统调度器立即切换到另一个可运行的线程。

  • 示例代码

    #include <iostream>
    #include <thread>
    
    void threadFunction() {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread running\n";
            std::this_thread::yield(); // 放弃处理器
        }
    }
    
    int main() {
        std::thread t(threadFunction);
        for (int i = 0; i < 5; ++i) {
            std::cout << "Main thread\n";
            std::this_thread::yield(); // 放弃处理器
        }
        t.join();
        return 0;
    }
    

    在这个示例中,yield() 函数被用来让出处理器,使得线程间能够交替执行。

std::this_thread::sleep_for()函数

  • 函数原型

    template< class Rep, class Period >
    void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration );
    
  • 参数意义sleep_duration 表示休眠的时间段,可以是 std::chrono::milliseconds, std::chrono::seconds 等类型。

  • 返回值void

  • 功能sleep_for() 函数会使当前线程休眠指定的时间段。

  • 示例代码

    #include <iostream>
    #include <thread>
    #include <chrono>
    
    int main() {
        std::cout << "Main thread starts sleeping\n";
        std::this_thread::sleep_for(std::chrono::seconds(3)); // 休眠3秒
        std::cout << "Main thread wakes up\n";
        return 0;
    }
    

    在这个示例中,主线程调用 sleep_for() 函数休眠3秒后再继续执行。

std::this_thread::sleep_until()函数

  • 函数原型

    template< class Clock, class Duration >
    void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
    
  • 参数意义sleep_time 表示将要休眠到的时间点,通常通过 std::chrono::system_clock::now() + duration 来计算。

  • 返回值void

  • 功能sleep_until() 函数会使当前线程休眠直到指定的时间点。

  • 示例代码

    #include <iostream>
    #include <thread>
    #include <chrono>
    
    int main() {
        auto wakeUpTime = std::chrono::system_clock::now() + std::chrono::seconds(5);
        std::cout << "Main thread starts sleeping\n";
        std::this_thread::sleep_until(wakeUpTime); // 休眠直到指定时间点
        std::cout << "Main thread wakes up\n";
        return 0;
    }
    

    在这个示例中,主线程调用 sleep_until() 函数休眠直到指定的时间点后再继续执行。

为什么thread的传参里面如果想传引用一定要用std::ref?

在C++中,std::ref是用于包装引用的模板函数,它位于<functional>头文件中。当你需要将一个引用传递给函数或者线程时,有时候需要使用std::ref来确保正确的引用传递语义。

通常情况下,当你将一个变量作为参数传递给函数或者线程时,会发生值复制。但是有时候你希望传递的实际上是引用本身,而不是引用指向的值。这时就可以使用std::ref来包装引用,以便进行正确的引用传递。

举个例子,假设有一个函数void func(int& val)接受一个整型引用作为参数,如果你想在创建线程时将某个整型变量x的引用传递给这个函数,可以这样做:

int x = 42;
std::thread t(func, std::ref(x));

在这个例子中,std::ref(x)x的引用包装起来,然后将这个包装后的引用传递给func函数,确保了在新线程中对x的引用传递。


介绍完ref后,我们来回答开始的问题:

在C++中,当你创建线程时,传递参数给线程的方式是通过复制参数值来实现的。如果你想传递引用而不是值,直接将引用传递给std::thread是不安全的,因为线程的执行可能会在原始变量(引用指向的对象)被销毁之后才开始,导致悬空引用或者未定义行为

使用std::ref包装引用的原因在于它可以延长被引用对象的生命周期std::ref返回一个std::reference_wrapper对象,该对象在复制时只是简单地复制了引用,而不是引用指向的内容。这样可以确保被引用对象在线程执行期间保持有效,并在线程完成后才会被销毁。

因此,为了避免悬空引用或者未定义行为,当你想要在线程中传递引用时,必须使用std::ref或者std::cref来保证被引用对象的有效性,从而确保线程安全地访问被引用的对象。

👉🏻mutex

基本接口函数

当涉及到多线程编程时,std::mutex 是一个常用的互斥量类,用于保护共享资源,避免多个线程同时访问导致数据竞争。以下是 std::mutex 类提供的一些主要接口函数:

  1. std::mutex 构造函数

    • 函数原型
      mutex();
      
    • 功能:创建一个新的互斥量对象。
  2. lock() 函数

    • 函数原型
      void lock();
      
    • 功能:尝试锁定互斥量,如果互斥量当前没有被其他线程锁定,则当前线程将锁定互斥量;如果互斥量已经被锁定,当前线程将被阻塞直到互斥量可用。
  3. try_lock() 函数

    • 函数原型
      bool try_lock();
      
    • 功能:尝试去锁定互斥量,如果互斥量当前没有被其他线程锁定,则当前线程将锁定互斥量并返回 true;如果互斥量已经被锁定,try_lock() 立即返回 false 而不会阻塞当前线程。
  4. unlock() 函数

    • 函数原型
      void unlock();
      
    • 功能:解锁互斥量,允许其他线程尝试锁定该互斥量。

下面是一个简单的示例,演示了如何使用 std::mutex 来保护共享资源:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedData = 0;

void updateSharedData() {
    mtx.lock();
    sharedData++;
    std::cout << "Thread ID: " << std::this_thread::get_id() << " updated sharedData to: " << sharedData << std::endl;
    mtx.unlock();
}

int main() {
    std::thread t1(updateSharedData);
    std::thread t2(updateSharedData);

    t1.join();
    t2.join();

    return 0;
}

在这个示例中,两个线程 t1t2 分别尝试更新 sharedData 变量,通过 std::mutex 对这一操作进行保护。这样可以确保在任一时刻只有一个线程可以访问和修改 sharedData,避免了数据竞争问题。

lock_guard和unique_lock

std::lock_guardstd::unique_lock 都是 C++ 中用于管理互斥锁(mutex)的 RAII(资源获取即初始化)类,它们可以帮助确保在作用域结束时自动释放互斥锁,避免忘记手动解锁而导致的死锁等问题。

  1. std::lock_guard

    • std::lock_guard 是一个轻量级的互斥锁封装类,一旦被创建,它会自动锁定传入的互斥锁,并在其作用域结束时自动解锁。
    • std::lock_guard 适用于那些在同一作用域内需要加锁和解锁的场景它不能手动释放锁,只能在作用域结束时自动释放锁。
  2. std::unique_lock

    • std::unique_lock 提供了比 std::lock_guard 更灵活的功能。它不仅可以管理互斥锁的锁定和解锁,还可以手动地进行锁定和解锁。
    • std::unique_lock 支持延迟加锁和条件变量,因此在需要更灵活控制锁的场景下更为适用。

在使用这两个类时,当创建一个 std::lock_guardstd::unique_lock 对象时,会在构造函数中锁定传入的互斥锁,并在对象销毁时(作用域结束)自动解锁。

以下是一个简单的示例,演示了如何使用 std::lock_guardstd::unique_lock 来管理互斥锁:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void workFunction() {
    std::lock_guard<std::mutex> lockGuard(mtx); // 使用 std::lock_guard 自动管理锁的加锁和解锁

    // 这里可以安全地访问共享资源,因为锁已经被自动加锁

    std::cout << "Worker thread is processing...\n";
}

int main() {
    std::thread workerThread(workFunction);

    // 主线程也可以使用 std::unique_lock 手动管理锁的加锁和解锁
    std::unique_lock<std::mutex> uniqueLock(mtx);
    
    // 这里可以安全地访问共享资源,因为锁已经被手动加锁

    // 在 uniqueLock 对象生命周期结束时,互斥锁会被自动解锁

    workerThread.join();

    return 0;
}

在这个示例中,workFunction 函数通过 std::lock_guard 管理互斥锁,而主线程通过 std::unique_lock 手动管理互斥锁


RAII(Resource Acquisition Is Initialization)是一种重要的 C++ 编程技术,用于管理资源的分配和释放。RAII 的核心思想是:通过在对象的构造函数中获取资源(如内存、文件句柄、互斥锁等),在对象的析构函数中释放资源,从而确保资源在对象生命周期结束时被正确释放。

使用 RAII 技术可以避免资源泄漏和忘记释放资源导致的问题,使得资源的管理更加安全和简单。当对象被创建时,它自动获取资源;当对象超出作用域时,其析构函数会自动调用,确保资源被释放。这种自动化的资源管理方式使得代码更加健壮并且易于维护。

常见的使用 RAII 的情况包括:

  1. 使用 std::unique_ptrstd::shared_ptr 等智能指针来管理动态分配的内存。
  2. 使用 std::lock_guardstd::unique_lock 等类来管理互斥锁的加锁和解锁。
  3. 使用文件流对象来管理文件的打开和关闭。
  4. 使用自定义的 RAII 类来管理其他资源,如数据库连接、网络连接等。

RTTI(Run-Time Type Information)是一个 C++ 的特性,用于在运行时获取对象的类型信息。通过 RTTI,你可以在程序运行时查询对象的实际类型,以及确定对象是否属于某个特定的类或子类。

C++ 中的 RTTI 主要通过两个关键字来实现:

  1. dynamic_cast:用于在继承体系中进行安全的向下转换(downcast),即将基类指针或引用转换为派生类指针或引用。如果转换不安全,dynamic_cast会返回空指针(对于指针)或抛出std::bad_cast异常(对于引用)。

  2. typeid:用于获取对象的类型信息。通过typeid操作符,可以获得一个指向std::type_info的对象,从而可以比较两个对象的类型是否相同。

这些特性允许你在运行时进行类型检查和类型转换,通常用于处理多态对象、工厂模式、插件系统等场景。需要注意的是,RTTI 的使用可能会引入一些运行时开销,并且过度依赖 RTTI 也可能暗示设计上的缺陷。因此,在使用 RTTI 时需要权衡好利弊,确保它符合程序设计的需要。

总的来说,RTTI 是 C++ 提供的一种功能强大的特性,可以在某些情况下帮助你更加灵活地处理对象的类型信息,但需要慎重使用以避免过度复杂化代码结构。


👉🏻condition_variable

当涉及到多线程编程中的线程同步和通信时,std::condition_variable 是一个重要的工具,用于实现线程间的条件变量等待和通知机制。下面是 std::condition_variable 类提供的一些主要接口函数:

  1. std::condition_variable 构造函数

    • 函数原型
      condition_variable();
      
    • 功能:创建一个新的条件变量对象。
  2. wait() 函数

    • 函数原型
      void wait(std::unique_lock<std::mutex>& lock);
      
    • 功能:当前线程等待条件变量,同时释放互斥锁 lock,使得其他线程可以获取互斥锁并修改共享数据。当收到通知后,wait() 函数会重新获取互斥锁 lock 并继续执行。
  3. notify_one() 函数

    • 函数原型
      void notify_one();
      
    • 功能:唤醒等待在条件变量上的一个线程,如果有多个线程在等待,则只会唤醒其中一个线程。
  4. notify_all() 函数

    • 函数原型
      void notify_all();
      
    • 功能:唤醒等待在条件变量上的所有线程。

下面是一个简单的示例,演示了如何使用 std::condition_variable 进行线程同步和通信:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void workerThread() {
    std::unique_lock<std::mutex> lock(mtx);
    while (!ready) {
        cv.wait(lock);
    }
    std::cout << "Worker thread is processing...\n";
}

int main() {
    std::thread worker(workerThread);

    // 模拟一些工作的完成
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        std::cout << "Main thread notifies worker thread\n";
    }
    
    cv.notify_one();

    worker.join();

    return 0;
}

在这个示例中,主线程通过设置 readytrue,并调用 notify_one() 唤醒等待在条件变量 cv 上的工作线程。工作线程在收到通知后被唤醒,然后开始处理任务。


当谈到条件变量在多线程中的作用时,我们可以通过一个幽默的例子来理解它的用处。

假设有一个办公室里有两个员工:小明和小红。他们的工作是协作完成一份文件,小明负责写文档,小红负责审核文档。他们需要保持协调,即小明写完了文档,小红要及时审核,并且在小红审核完后,小明要能够继续写新的文档。

这里,小明和小红就相当于两个线程,他们需要协作完成工作,而条件变量就是用来协调这种工作流程的。具体来说:

  • 小明写完文档后,他会等待条件变量,即等待小红的审核通知。
  • 小红审核完文档后,她会通知小明,同时唤醒小明继续写新的文档。

这个例子中,小明和小红就像是两个线程,他们通过条件变量来同步工作,保证了写和审核工作的顺利进行,避免了资源的浪费和不必要的等待。同时,这个例子也生动地展示了条件变量在多线程场景下的作用,希望能够帮助你更好地理解条件变量的使用。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

【Linux第三课-基础开发工具的使用】yum、vim、gcc/g++编译器、gdb、Make/Makefile编写、进度条程序、git命令行简单操作

目录 yum - 软件包管理器快速认识yum快速使用yumyum搜索yum安装yum卸载 yum的周边 - yum的整个生态问题 vim快速介绍vimvim的模式命令模式插入模式低行模式 常见模式 -- 命令、低行命令模式 -- 光标的移动命令模式 -- 复制粘贴、剪贴、删除命令模式 -- 小写/大写替换模式命令模…

单片机-- 数电(3)

编码器与译码器 译码 &#xff1a;将二进制代码转化为其他进制的代码 编码 &#xff1a;就是将其他代码转换为二进制码 编码器的类型 1二进制编码器 用n位二进制数码对2的n次方个输入信号进行编码的电路 2二-十进制编码器 将0到9十个十进制数转化为二进制代码的电路 2…

PyTorch 深度学习(GPT 重译)(三)

六、使用神经网络拟合数据 本章内容包括 与线性模型相比&#xff0c;非线性激活函数是关键区别 使用 PyTorch 的nn模块 使用神经网络解决线性拟合问题 到目前为止&#xff0c;我们已经仔细研究了线性模型如何学习以及如何在 PyTorch 中实现这一点。我们专注于一个非常简单…

Python 解析CSV文件 使用Matplotlib绘图

数据存储在CSV文件中&#xff0c;使用Matplotlib实现数据可视化。 CSV文件&#xff1a;comma-separated values&#xff0c;是在文件中存储一系列以‘&#xff0c;’分隔的值。 例如&#xff1a;"0.0","2016-01-03","1","3","20…

性能测试-Jmeter中IF控制器使用

一、Jmeter控制器 分为两种类型&#xff1a; 控制测试计划执行过程中节点的逻辑执行顺序&#xff0c;如&#xff1a;循环控制器&#xff0c;if控制器等对测试计划中的脚本进行分组&#xff0c;方便Jmeter统计执行结果以及进行脚本的运行时控制等&#xff0c;如&#xff1a;吞…

【ann2coco】图像label转coco格式的JSON

目录 &#x1f34b;&#x1f34b;需求 &#x1f34b;&#x1f34b;coco格式 &#x1f34b;&#x1f34b;python代码实现 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 送你们一条美丽的--分割线-- &#x1f34b;&#x1f34b;需求 单波段灰度图l…

7-6 混合类型数据格式化输入

题目链接&#xff1a;7-6 混合类型数据格式化输入 一. 题目 1. 题目 2. 输入输出格式 3. 输入输出样例 4. 限制 二、代码 1. 代码实现 #include <stdio.h>int main(void) {int num;char c;float f1,f2;if (scanf("%f %d %c %f", &f1, &num, &c…

【NLP笔记】预训练+微调范式之OpenAI Transformer、ELMo、ULM-FiT、Bert..

文章目录 OpenAI TransformerELMoULM-FiTBert基础结构Embedding预训练&微调 【原文链接】&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 【本文参考链接】 The Illustrated BERT, ELMo, and co. (How NLP Cracked Tra…

单片机LED灯闪烁

延时函数计算&#xff08;相关代码生成&#xff09;&#xff1a; #include "reg52.h" #include <INTRINS.H> void Delay500ms() //11.0592MHz {unsigned char i, j, k;_nop_();_nop_();i 22;j 3;k 227;do{do{while (--k);} while (--j);} while (--i); }vo…

以太网协议(数据链路层)

一些基础知识: 平时使用的网线,也叫做"以太网线".平时使用的交换机,也叫做"以太网交换机".以太网这个协议,既涉及到数据链路层的内容,也涉及到物理层的内容. 1. 以太网帧的格式 ①目的地址,源地址: 此处的地址,叫做mac地址(物理地址).作用也是区分不同…

水果软件FL Studio 21 for mac 21.2.3.3586破解版的最新版本2024介绍安装

音乐是人类最美好的语言&#xff0c;它能够跨越国界、文化和语言&#xff0c;将人们紧密地联系在一起。在当今数字化时代&#xff0c;音乐创作已经不再是专业人士的专利&#xff0c;越来越多的音乐爱好者开始尝试自己动手制作音乐。而FL Studio21中文版编曲软件正是这样一个为你…

Linux:点命令source

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 source命令用于读取一个文件的内容并在当前Shell环境&#xff08;包括交互式Shell或是非交互式Shell&#xff09;执行里面的命令。它被称为点命令是因为命令名source也可…

【Web应用技术基础】HTML(4)——表单类的标签

目录 题目1&#xff1a;文本框 题目2&#xff1a;密码框 题目3&#xff1a;单选框 题目4&#xff1a;多选框 题目5&#xff1a;单选框选中 题目6&#xff1a;禁用disabled 题目7&#xff1a;lable标签 题目8&#xff1a;下拉框 题目9&#xff1a;textarea 题目10&…

【STM32】读写BKP备份寄存器RTC实时时钟

目录 BKP BKP简介 BKP基本结构 BKP测试代码 RTC RTC简介 RTC框图 RTC基本结构 硬件电路 RTC操作注意事项 接线图 初始化 使用BKP解决只初始化一次时间 初始化参考代码 RTC设置时间 RTC读取时间 完整代码 MyRTC.c MyRTC.h main.c BKP BKP简介 BKP&#xff0…

05.自定义指令,插槽和路由配置

一、学习目标 1.自定义指令 基本语法&#xff08;全局、局部注册&#xff09;指令的值v-loading的指令封装 2.插槽 默认插槽具名插槽作用域插槽 3.综合案例&#xff1a;商品列表 MyTag组件封装MyTable组件封装 4.路由入门 单页应用程序路由VueRouter的基本使用 一、自…

基于 HBase Phoenix 构建实时数仓(5)—— 用 Kafka Connect 做实时数据同步

目录 一、总体架构 二、安装配置 MySQL 1. 创建 mysql 用户 2. 建立 MySQL 使用的目录 3. 解压安装包 4. 配置环境变量 5. 创建 MySQL 配置文件 6. MySQL 系统初始化 7. 启动 mysql 服务器 8. 创建 dba 用户 三、配置 MySQL 主从复制 四、安装部署 Kafka Connector…

【机器学习】k近邻(k-nearest neighbor )算法

文章目录 0. 前言1. 算法原理1.1 距离度量1.2 参数k的选择 2. 优缺点及适用场景3. 改进和扩展4. 案例5. 总结 0. 前言 k近邻&#xff08;k-nearest neighbors&#xff0c;KNN&#xff09;算法是一种基本的监督学习算法&#xff0c;用于分类和回归问题。k值的选择、距离度量及分…

(2024,YOSO,自协同学习,扩散 GAN,单步生成)您只需采样一次:通过自协同扩散 GAN 驯服一步文本到图像合成

You Only Sample Once: Taming One-Step Text-To-Image Synthesis by Self-Cooperative Diffusion GANs 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 相关工作 3. 背景…

Vue3 中应该使用 Ref 还是 Reactive?

一、引言 在Vue 3中&#xff0c;构建响应式数据结构是构建用户界面和交互体验的核心部分。而在创建这些响应式数据时&#xff0c;我们有两个主要工具&#xff1a;reactive和ref。选择使用哪一个&#xff0c;实际上取决于你的数据结构和访问需求。 reactive主要用于处理复杂的数…

麒麟 V10 一键安装 Oracle 19C 19.22 单机版

Oracle 一键安装脚本&#xff0c;演示 麒麟 V10 一键安装 Oracle 19C 19.22 单机版过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&#xff1a;Shell脚本安装Oracle数据库 脚…