施磊老师高级c++(四)

c++11 知识点汇总

文章目录

  • c++11 知识点汇总
    • 一、C++11常用关键知识点梳理
      • 1. 关键字和语法
      • 2. 绑定器和函数对象
      • 3. 智能指针
      • 4. 容器
    • 二、C++语言级别支持的多线程编程 -- 本节重点
      • 1. 通过thread类编写C++多线程程序
      • 2. 线程间互斥锁与死锁 -- 对应linux多线程互斥锁
        • 代码:
        • **竞态条件**
        • 线程互斥mutex---- 跟linux使用非常像
        • 死锁问题
          • lock_guard-保证所有线程都能释放锁
          • unique_lock
      • 3. 线程间同步通信-生产者消费者模型
        • **生产者-消费者**线程模型
          • 错误案例
          • 正确案例-- 使用条件变量
      • 2.4 再谈lock_guard和unique_lock
      • 2.5 基于CAS操作的atomic原子类型
      • 2.6额外补充:CAS的成员方法---课里没讲
        • **基本语法和使用示例**
        • **CAS 操作解释:**
        • **总结**:

一、C++11常用关键知识点梳理

1. 关键字和语法

  1. auto:可以根据右值,推导出右值的类型,然后左边变量的类型就已知了

  2. nullptr:给指针专用(能够和整数进行区别);之前的NULL是一个宏定义#define NULL 0,在代码上无法区分整数和指针地址

  3. foreach

    语句:可以遍历数组(底层是指针遍历),容器(底层是迭代器遍历)等

    for(Type val : container) => 底层就是通过指针或者迭代器来实现的cout<<val<<" ";
    
  4. 右值引用:move移动语义函数和forward类型完美转发函数

  5. 模板的一个新特性:typename... A 表示可变参(类型参数)

2. 绑定器和函数对象

  • function:函数对象
  • bind:绑定器
  • bind1stbind2nd 只能结合 二元函数对象=>一元函数对象 – 非常有限
  • lambda表达式

3. 智能指针

shared_ptrweak_ptr----带引用计数, ---- 分强弱智能指针, weak_ptr指针.lock()可以提升为强智能指针
不带引用计数虽然有好几个, 但是 推荐使用 unique_ptr

4. 容器

  • set和map:红黑树,增删查O(logn) – 以前c++标准库就有
  • unorder_set和unorder_map:哈希表,增删查O(1) ---- c++11 新增
  • array:数组,固定大小,不可扩容。区别于vector ---- c++11 新增, 需要确保数量已知
  • forward_list:前向链表,单链表。list是双向链表 ---- c++11 新增

推荐使用vector和list, 更灵活, 具体情况具体看待

二、C++语言级别支持的多线程编程 – 本节重点

linux下的 线程函数, 在c++里并不适用

C++语言级别的多线程编程 =>代码可以跨平台:windows/linux/mac

主要内容: thread/mutex/condition_variable–线程, 互斥, 条件变量
: lock_quard, unique_lock
原子类型: atomic
睡眠: sleep_for

C++语言层面 thread--可以根据系统, 使用不同的底层(底层用的还是下面平台的方法) windows        linux:|             |
createThread    pthread_create

需要包含的头文件#include <thread>-----linux是 pthread

1. 通过thread类编写C++多线程程序

  1. 主要函数:

    std::thread: 创建线程对象, ---- 类似于 pthread_create

    std::this_thread::sleep_for(std::chrono::seconds(2)) : 线程睡眠2s – 类似于 sleep(2)

    线程对象.join : 回收等待子线程, 在c++里 不分离线程的化, 必须回收等待, 这个与 linux不同----非常严格

    线程对象.detach: 分离线程

  2. 怎么创建启动一个线程?
    std::thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启

  3. 子线程如何结束?
    子线程函数运行完成,线程就结束了

  4. 主线程如何处理子线程?
    t.join():等待t线程结束,当前线程继续往下运行
    t.detach():把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束,类似于守护线程

#include <iostream>
#include <thread>
using namespace std;void threadHandle1(int time)
{//让子线程睡眠time秒std::this_thread::sleep_for(std::chrono::seconds(time));cout << "hello threadHandle1!" << endl;
}void threadHandle2(int time)
{//让子线程睡眠time秒std::this_thread::sleep_for(std::chrono::seconds(time));cout << "hello threadHandle2!" << endl;
}int main()
{// 创建了一个线程对象,传入一个线程函数(作为线程入口函数), 新线程就开始运行,没有先后顺序,随着CPU的调度算法执行std::thread t1(threadHandle1, 1);std::thread t2(threadHandle2, 2);// 主线程运行到这里,等待子线程结束,主线程继续往下运行t1.join();t2.join();// 把子线程设置为分离线程//t1.detach();//t2.detach();cout << "main thread done!" << endl;/*主线程运行完成时,会查看当前进程是否还有未运行完成的子线程如果有未运行完成的子线程,那么进程就会异常终止*/return 0;
}

2. 线程间互斥锁与死锁 – 对应linux多线程互斥锁

c++ thread 模拟车站三个窗口买票的 程序

代码:

– 不加互斥锁, 数据是乱的, 会同时同一时间 访问 某个量

#include <iostream>
#include <thread>
#include <list>
using namespace std;/*
c++ thread 模拟车站三个窗口买票的 程序
*/int ticketCount = 10; // 车站有10张车票,由三个窗口一起卖票// 模拟卖票的线程函数
void sellTicket(int index)
{while (ticketCount > 0){cout << "窗口:" << index << "卖出第:" << 11-ticketCount << "张票!" << endl;ticketCount--;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{list<std::thread> tlist;  // 使用双向链表for (int i = 1; i <= 3; ++i){tlist.push_back(std::thread(sellTicket, i));}for (auto& t : tlist){t.join();}cout << "所有窗口卖票结束!" << endl;return 0;
}
竞态条件

指的是多个线程或进程同时访问共享资源时,程序的执行结果依赖于线程或进程的执行顺序,从而导致不可预测的行为或错误。

线程互斥mutex---- 跟linux使用非常像

要对线程安全进行保障,这就需要线程间的互斥,使用互斥锁,需要包含头文件#include <mutex>

.lock()

.unlock()

注意 : 加锁和解锁的位置! 非常影响打印的效果

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;/*
c++ thread 模拟车站三个窗口买票的 程序
*/int ticketCount = 10; // 车站有10张车票,由三个窗口一起卖票std::mutex mtx; // 全局的一把互斥锁// 模拟卖票的线程函数
void sellTicket(int index)
{//mtx.lock();//这里是不行的, 导致 一个线程 把票全卖了while (ticketCount > 0){mtx.lock();if (ticketCount > 0)  // 必须再次判断, 因为是先进来循环,才等锁, 会引发另一个卖完了, 这个还在循环里, 还会卖{cout << "窗口:" << index << "卖出第:" << 11 - ticketCount << "张票!" << endl;ticketCount--;}mtx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(100));}}int main()
{list<std::thread> tlist;  // 使用双向链表for (int i = 1; i <= 3; ++i){tlist.push_back(std::thread(sellTicket, i));}for (auto& t : tlist){t.join();}cout << "所有窗口卖票结束!" << endl;return 0;
}

临界区(Critical Section) 是多线程编程中的一个重要概念,指的是访问共享资源(如变量、数据结构、文件等)的一段代码。临界区中的代码需要被保护,以确保同一时间只有一个线程可以执行这段代码,从而避免竞态条件(Race Condition)和数据不一致的问题。

要保证临界区代码段 原子操作

死锁问题

程序如果在在中间出现问题, unlock就不会执行到了,会被阻塞加锁那里, 会导致死锁.

c++11 提供了lock_guardunique_lock 解决死锁问题

lock_guard-保证所有线程都能释放锁

lock_guardlock_guard<std::mutex> lock(mutex锁名); 构造会自动上锁,析构会自动释放。拷贝构造与赋值重载函数被删除掉了,类比智能指针scoped_ptr

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;/*
c++ thread 模拟车站三个窗口买票的 程序
*/int ticketCount = 10; // 车站有10张车票,由三个窗口一起卖票std::mutex mtx; // 全局的一把互斥锁// 模拟卖票的线程函数
void sellTicket(int index)
{//mtx.lock();//这里是不行的, 导致 一个线程 把票全卖了while (ticketCount > 0){//mtx.lock();{lock_guard<std::mutex> lock(mtx);   // 出作用域自动析构解锁if (ticketCount > 0){cout << "窗口:" << index << "卖出第:" << 11 - ticketCount << "张票!" << endl;ticketCount--;}}//mtx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(100));}}int main()
{list<std::thread> tlist;  // 使用双向链表for (int i = 1; i <= 3; ++i){tlist.push_back(std::thread(sellTicket, i));}for (auto& t : tlist){t.join();}cout << "所有窗口卖票结束!" << endl;return 0;
}
unique_lock

unique_lock:构造会自动上锁,析构会自动释放。拷贝构造与赋值重载函数被删除掉了,提供了带右值引用版本的,类比智能指针unique_ptr

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;/*
c++ thread 模拟车站三个窗口买票的 程序
*/int ticketCount = 10; // 车站有10张车票,由三个窗口一起卖票std::mutex mtx; // 全局的一把互斥锁// 模拟卖票的线程函数
void sellTicket(int index)
{//mtx.lock();//这里是不行的, 导致 一个线程 把票全卖了while (ticketCount > 0){//mtx.lock();{//lock_guard<std::mutex> lock(mtx);   // 出作用域自动析构解锁unique_lock<std::mutex> lock(mtx);   // 出作用域自动析构解锁if (ticketCount > 0){cout << "窗口:" << index << "卖出第:" << 11 - ticketCount << "张票!" << endl;ticketCount--;}}//mtx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(100));}}int main()
{list<std::thread> tlist;  // 使用双向链表for (int i = 1; i <= 3; ++i){tlist.push_back(std::thread(sellTicket, i));}for (auto& t : tlist){t.join();}cout << "所有窗口卖票结束!" << endl;return 0;
}

3. 线程间同步通信-生产者消费者模型

多线程编程两个问题:

  1. 线程间的互斥

    竞态条件导致=>对临界区代码段=>其原子操作=>添加互斥锁mutex、轻量级的无锁实现(CAS)

    Linux下strace ./a.out(程序启动的跟踪打印的命令)会发现 c++代码底层使用的还是 linux的 pthread_mutex_t

  2. 线程间的同步通信

    线程间不通信的话,每个线程受CPU的调度,没有任何执行上的顺序可言,线程1和线程2是根据CPU调度算法来的,两个线程都有可能先运行,是不确定的,线程间的运行顺序是不确定的

通信就是:

  • 线程1和线程2一起运行,线程2要做的事情必须先依赖于线程1完成部分的事情,然后线程1告诉线程2这部分东西做好了,线程2就可以继续向下执行了
  • 或者是线程1接下来要做某些操作,这些操作需要线程2把另外一部分事情做完,然后通知一下线程1它做完了,然后线程1才能做这些操作。
生产者-消费者线程模型

注意C++ STL中所有的容器都不是线程安全的,都需要进行封装。在这个例子中把queue封装成了Queue

错误案例

先看一个非常简便的 例子 : 这个例子 问题很多:生产者空,消费者还要消费
生产者和消费者 不交流

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <queue>
using namespace std;std::mutex mtx;// 生产者生产一个物品,通知消费者消费一个;消费完了,消费者再通知生产者继续生产物品
class Queue
{
public://生产物品void put(int val){lock_guard<std::mutex> suo(mtx);que.push(val);cout << "生产者 生产:" << val <<"号物品" << endl;}// 消费物品int get(){lock_guard<std::mutex> suo(mtx);int val = que.front();que.pop();cout << "消费者 消费:" << val <<"号物品" << endl;return val;}private:queue<int> que;};void producer(Queue* que)	// 生产者线程
{for (int i = 1; i <= 10; ++i){que->put(i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer(Queue* que)	// 消费者线程
{for (int i = 1; i <= 10; ++i){que->get();std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}int main()
{Queue que;	// 两个线程共享的队列std::thread t1(producer, &que);std::thread t2(consumer, &que);t1.join();t2.join();return 0;
}
正确案例-- 使用条件变量

信号量 (这是指c中的信号量)虽然可以做, 但是 条件变量(c里有, 但是c++11提供了更好的封装)更好用

条件变量:有两种

  • 功能:条件变量允许线程等待某个条件为真时才继续执行,通常与互斥锁结合使用。

  • 使用条件变量需要包含头文件#include <condition_variable>

  • .wait() 必须传入 unique_lock 类型的 加锁, 不能是别的类型

  • std::condition_variable

    • 必须与 std::unique_lock<std::mutex> 配合使用。
    • 性能更高,但灵活性较低。
  • std::condition_variable_any

    • 可以与任何满足基本要求的锁类型配合使用。
    • 灵活性更高,但性能较低。
  • 核心操作:

    条件变量的核心操作
    wait():使当前线程进入等待状态,直到被通知。通常与谓词(Predicate)一起使用,以避免虚假唤醒。notify_one():唤醒一个等待的线程。notify_all():唤醒所有等待的线程。
    
  • 条件变量, 虽然没有明确的 状态术语, 不过 一般来说 : wait() 使线程进入等待状态,在此期间它会释放关联的互斥锁并挂起执行,直到收到 notify_one()notify_all() 的通知后被唤醒,并尝试重新获取锁;而阻塞状态指的是线程在尝试获取锁时发现锁已被其他线程持有,因此无法继续执行,必须等待锁释放后才能继续运行。
    通知后, 其它线程得到该通知,就会从等待状态(条件达成)=>阻塞状态,之后获取互斥锁继续执行----有点乱, 大概明白就行

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <queue>
#include <condition_variable>
using namespace std;std::mutex mtx;  // 定义互斥锁, 线程间的 同步操作
std::condition_variable cv; // 定义条件变量 线程间的 通信操作// 生产者生产一个物品,通知消费者消费一个;消费完了,消费者再通知生产者继续生产物品
class Queue
{
public://生产物品void put(int val){/*lock_guard<std::mutex> guard(mtx);*/// que不为空,生产者应该通知消费者去消费unique_lock<std::mutex> lck(mtx);while (!que.empty())  {// que不为空,生产者应该通知消费者去消费, 消费者消费完了,生产者再继续生产// 生产者线程进入#1等待状态,并且#2把mtx互斥锁释放掉cv.wait(lck); //传入一个互斥锁,当前线程挂起,处于等待状态,并且释放当前锁}que.push(val);cv.notify_all();  // 通知其他线程 , 生产了物品, 可以消费//其它线程得到该通知,就会从等待状态变为阻塞状态,之后获取互斥锁继续执行cout << "生产者 生产:" << val <<"号物品" << endl;}// 消费物品int get(){/*lock_guard<std::mutex> guard(mtx);*/unique_lock<std::mutex> lck(mtx);//消费者线程发现que是空的,通知生产者线程先生产物品//消费者线程进入等待状态,并且把mtx互斥锁释放掉while (que.empty()){cv.wait(lck); // 循环等待, 并释放互斥锁}int val = que.front();que.pop();cv.notify_all();cout << "消费者 消费:" << val <<"号物品" << endl;return val;}private:queue<int> que;
};void producer(Queue* que)	// 生产者线程
{for (int i = 1; i <= 10; ++i){que->put(i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer(Queue* que)	// 消费者线程
{for (int i = 1; i <= 10; ++i){que->get();std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}int main()
{Queue que;	// 两个线程共享的队列std::thread t1(producer, &que);std::thread t2(consumer, &que);t1.join();t2.join();return 0;
}

**特别注意: **

  • 锁的位置对多线程程序的正确性至关重要。
  • 锁必须覆盖整个操作,包括对共享资源的访问和条件变量的等待。
  • 如果将锁放到 while 循环内部,会导致竞争条件和输出乱序。
  • 正确的锁位置应覆盖整个操作,确保线程间的同步和共享资源的安全性。

2.4 再谈lock_guard和unique_lock

主要是为了再讲讲lock_guard和unique_lock, condition_varivable, wait,notify_all

std::mutex mtx;mtx.lock();mtx.unlock();

普通互斥锁的缺点: 有可能中间走掉了,导致没有unlock(),不安全

lock_guard<std::mutex> guard(mtx); 可以出了作用域 自动析构

lock_guard不可能用在函数参数传递或者返回过程中,因为拷贝构造和赋值函数都被删除了

只能用在简单的加锁解锁临界区代码段的互斥操作中,出作用域析构自动释放锁

unique_lock<std::mutex> lck(mtx);搭配条件变量使用

unique_lock : 不仅可以使用在简单的加锁解锁临界区代码段的互斥操作中,还能用在函数调用过程中,因为其虽然删除了拷贝构造和赋值函数,但是提供了带右值引用版本的

总结: lock_guard 适用于 无线程通信的 情况

unique_lock 搭配 condition_variable使用, 可以 wait和notify_all,notify_one 结合使用

/*
通知在cv上等待的线程,条件成立了,起来干活了!
其它在cv上等待的线程,收到通知,
从等待状态 -> 到阻塞状态(不能直接运行) 
只有当前线程释放锁了,其他线程获取互斥锁了,线程才能继续往下执行
*/
cv.notify_all();

2.5 基于CAS操作的atomic原子类型

本节重点是: atomic 这个模板类定义的原子类型变量 , 这种类型的变量的操作都将是 是原子操作

这意味着:
std::atomic 类型的变量进行的所有操作(例如读取、写入、增减等)都是不可中断的,不会被其他线程的操作打断。

  1. 窗口卖票的代码, count+±- 操作, 是线程不安全的, 之前 使用了 互斥锁 保证线程安全

  2. 互斥锁是比较重的,临界区代码复杂时可以使用;但现在我们只是做一个加加减减的操作,还是需要一些轻量级原子操作的操作
    解决办法:使用CAS保证上面加加减减操作的原子特性就足够了,CAS也叫做无锁操作

  3. 什么是 原子操作?

    原子操作(Atomic Operation)指的是一系列操作在执行期间不可被中断的操作,它是一个不可分割的操作单元。也就是说,当一个原子操作开始执行时,它要么完全执行成功,要么完全不执行,不会被其他线程中断或打断,保证了操作的完整性和一致性。

  4. 什么是CAS?

    CAS(Compare-And-Swap) 是一种原子操作,用于在多线程环境中实现无锁的线程同步。其核心概念是:比较和交换(exchange/swap),即在执行操作时,先比较目标变量的当前值与预期值是否相等,如果相等则将目标变量的值替换为新值,否则不做任何修改。
    CAS(Compare-And-Swap) 是一种原子操作,用于实现无锁编程(Lock-Free Programming)。它是一种硬件级别的同步机制,通常用于多线程环境中,确保对共享数据的操作是原子的(即不可分割的)。

  5. CAS 就是无锁操作, 面试的 无锁队列啥的, 就是CAS

  6. std::atomic 这是一个模板, 所以需要搭配<>使用

  7. volatile 是 C 和 C++ 中的一个关键字,用于告诉编译器某个变量的值可能会在程序外部发生变化,因此编译器在优化时不能对该变量进行某些假设(禁止优化),必须每次直接从内存中读取其值。 防止缓存

  8. 原子操作: 实际就是 所有的读写 不会被其他线程中断

  9. std::atomic_bool 是 C++11 标准引入的一个别名类型,它是 std::atomic<bool> 的简写或别名。std::atomic_bool 在 C++ 中是一个 类型别名,通常用于让代码更加简洁,便于编写与使用。

头文件:#include <atomic>

代码示例:

#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <queue>
#include <condition_variable>
using namespace std;volatile std::atomic_bool isReady = false;  // atomic的特例化版本//volatile std::atomic_int ticketCount = 0;  // volatile  防止多线程变量 进行缓存
volatile std::atomic<int> ticketCount = 0;  // 一般这么用 void task()
{while (!isReady)std::this_thread::yield(); // 让当前线程 自愿地让出 CPU 的控制权(cpu时间片),允许同一线程的其他线程获得执行机会for (int i = 0; i < 100; ++i)ticketCount++;
}int main()
{list<std::thread> tlist;for (int i = 0; i < 10; ++i)tlist.push_back(std::thread(task));std::this_thread::sleep_for(std::chrono::seconds(1));cout << "ticketCount:" << ticketCount << endl;	// ticketCount:0isReady = true;  // 这里再执行++for (auto& t : tlist)t.join();cout << "ticketCount:" << ticketCount << endl;	// ticketCount:1000return 0;
}

2.6额外补充:CAS的成员方法—课里没讲

CAS(Compare-And-Swap) 是一种原子操作,用于在并发编程中保证线程安全,常用于实现无锁数据结构和算法。它的基本原理是比较某个变量的当前值是否等于预期值,如果相等,则交换为新的值。这个操作是原子的,因此能有效避免线程竞争。

在 C++ 中,CAS 通常通过标准库中的 std::atomic 来实现,它提供了原子操作的接口,包括 compare_exchange_weakcompare_exchange_strong 两种形式。

基本语法和使用示例

假设你有一个 std::atomic<int> 类型的变量,并且你想使用 CAS 来修改它。

#include <iostream>
#include <atomic>int main() {std::atomic<int> x(5); // 创建一个原子整数,初始值为5int expected = 5;       // 预期值为5int new_value = 10;     // 新值为10// 使用 compare_exchange_strong 执行 CAS 操作bool success = x.compare_exchange_strong(expected, new_value);if (success) {std::cout << "CAS 操作成功,新的值是: " << x.load() << std::endl;} else {std::cout << "CAS 操作失败,当前值是: " << x.load() << std::endl;}return 0;
}
CAS 操作解释:
  1. x.compare_exchange_strong(expected, new_value)
    • 比较 x 的当前值和 expected 的值。如果它们相等,x 将被更新为 new_value,并返回 true
    • 如果 x 的当前值不等于 expected,则 expected 将被更新为 x 的当前值,操作失败,返回 false
    • compare_exchange_strong 是一种强操作,它会进行多个重试,直到操作成功或遇到某些停止条件。
  2. expected 变量在 CAS 操作后,可能会被修改为 x 当前的值,因此你需要在失败时查看 expected 的新值。

compare_exchange_weak vs compare_exchange_strong

  • compare_exchange_strong:确保操作尽可能成功地执行,可能会进行多次重试,直到操作成功。
  • compare_exchange_weak:不保证每次都能执行,可能会失败并返回 false,适用于无锁算法中可能需要退让的情形。

代码解析:

  • 成功的 CAS 操作expectedx 的值相等时,x 会被更新为新值,successtrue
  • 失败的 CAS 操作expectedx 的值不相等时,expected 会更新为 x 的当前值,successfalse

注意事项:

  1. ABA 问题:CAS 操作可能会出现 ABA 问题,指的是一个值从 A 改为 B,然后又变回 A,这时 CAS 可能会误认为值没有变化。解决方法之一是使用带有版本号的 CAS 或者增加标记。
  2. 高并发问题:CAS 操作可能导致忙等(自旋),如果操作频繁失败,可能会消耗大量的 CPU 资源。此时,可以考虑结合自旋锁或引入等待机制。
总结
  • CAS 是一种高效的原子操作,用于并发编程中无锁算法的实现。
  • 在 C++ 中,std::atomic 提供了 CAS 操作的接口,如 compare_exchange_strongcompare_exchange_weak
  • 使用 CAS 时,需要注意 ABA 问题和自旋的效率问题。

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

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

相关文章

C/C++蓝桥杯算法真题打卡(Day5)

一、P8772 [蓝桥杯 2022 省 A] 求和 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> // 包含标准库中的所有头文件&#xff0c;方便编程 using namespace std; // 使用标准命名空间&#xff0c;避免每次调用标准库函数时都要加 std::int main() {int n; …

【大模型基础_毛玉仁】3.5 Prompt相关应用

目录 3.5 相关应用3.5.1 基于大语言模型的Agent3.5.2 数据合成3.5.3 Text-to-SQL3.5.4 GPTs 3.5 相关应用 Prompt工程应用广泛&#xff0c;能提升大语言模型处理基础及复杂任务的能力&#xff0c;在构建Agent、数据合成、Text-to-SQL转换和设计个性化GPTs等方面不可或缺。 . …

主成分分析PCA与奇异值分解SVD

线性代数 SVD 奇异值分解&#xff08;Singular Value Decomposition&#xff0c;简称 SVD&#xff09;是线性代数中的一种基本工具&#xff0c;它将任意一个 (m * n) 矩阵 (A) 分解成三个简单矩阵的乘积&#xff0c;即 其中&#xff1a; (U) 是一个 (m*m) 的正交&#xff08…

自主代理的摩尔定律:AI 的指数级革命

图像由 Gemini 生成 前言&#xff1a;AI 正在以超过摩尔定律的速度迅速提升其自主工作能力&#xff0c;研究显示&#xff0c;AI 能够可靠完成的任务时长正以每 7 个月翻一倍的速度增长。这种指数级的发展趋势意味着&#xff0c;AI 不再只是应对简单问答或短任务的工具&#xff…

气膜文化馆:打造沉浸式文娱新空间—轻空间

演唱会、展览、音乐剧……都能办&#xff1f; 当然&#xff01;气膜文化馆不仅适用于体育赛事&#xff0c;在文化娱乐方面同样大放异彩&#xff01; 声学优化&#xff0c;打造极致听觉体验 气膜文化馆采用专业声学设计&#xff0c;避免传统场馆的回声干扰&#xff0c;提供更清…

【数据标准】数据标准化框架体系-对象类数据标准

导读&#xff1a;对象类数据标准化框架通过统一数据定义、分类和标记&#xff0c;解决数据孤岛与不一致问题&#xff0c;支撑数据分析、AI应用与合规需求。企业需结合自身业务特性&#xff0c;灵活选择国际标准&#xff08;如ISO&#xff09;、行业规范或自建体系&#xff0c;并…

【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)

SPI通信协议及S为5Q64简介&#xff1a;【STM32】SPI通信协议&W25Q64Flash存储器芯片&#xff08;学习笔记&#xff09;-CSDN博客 STM32与W25Q64模块接线&#xff1a; SPI初始化&#xff1a; 片选SS、始终SCK、MOSI都是主机输出引脚&#xff0c;输出引脚配置为推挽输出&…

C 语 言 --- 扫 雷 游 戏(初 阶 版)

C 语 言 --- 扫 雷 游 戏 初 阶 版 代 码 全 貌 与 功 能 介 绍扫雷游戏的功能说明游 戏 效 果 展 示游 戏 代 码 详 解game.htest.cgame.c 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 人 主…

数据库基础知识

目录 一、什么是数据库&#xff1f; 二、基本使用方法 &#xff08;1&#xff09;启动服务器进程 &#xff08;2&#xff09;连接服务器 &#xff08;3&#xff09;基本sql语句 三、MySQL架构 四、SQL语句分类 五、存储引擎是什么 一、什么是数据库&#xff1f; 数据库…

在线生成自定义二维码

在线生成自定义二维码 1. 引言 二维码已成为现代互联网的重要工具&#xff0c;广泛应用于链接分享、支付、身份认证等场景。然而&#xff0c;很多在线二维码生成工具功能有限&#xff0c;难以满足个性化需求。如果你需要 自定义颜色、Logo、不同形状的二维码&#xff0c;那么…

DeepSeek处理多模态数据的技术要点和实现方式

DeepSeek具备处理多模态数据的能力&#xff0c;以下是相关技术要点和实现方式。 1. ‌多模态模型架构‌ ‌单流/双流网络‌&#xff1a;通过将文本和图像输入统一编码器&#xff08;单流&#xff09;或分别编码后交互&#xff08;双流&#xff09;实现模态融合‌。‌预训练模…

系统架构设计知识体系总结

1.技术选型 1.什么是技术选型&#xff1f; 技术选型是指评估和选择在项目或系统开发中使用的最合适的技术和工具的过程。这涉及考虑基于其能力、特性、与项目需求的兼容性、可扩展性、性能、维护和其他因素的各种可用选项。技术选型的目标是确定与项目目标相符合、能够有效解…

数智读书笔记系列022《算力网络-云网融合2.0时代的网络架构与关键技术》读书笔记

一、书籍核心价值与定位 1.1 书籍概述:中国联通研究院的权威之作 《算力网络 —— 云网融合 2.0 时代的网络架构与关键技术》由中国联通研究院算力网络攻关团队精心撰写,是业界首部系统性探讨云网融合 2.0 与算力网络的专著。在云网融合从 1.0 迈向 2.0 的关键节点,本书的…

知识图谱中NLP新技术

知识图谱与自然语言处理&#xff08;NLP&#xff09;的结合是当前人工智能领域的前沿方向&#xff0c;其技术发展呈现多维度融合与场景深化的特点。以下从核心技术突破、应用场景创新及未来趋势三个层面&#xff0c;系统梳理知识图谱中NLP的最新进展&#xff1a; 一、核心技术突…

ASP.NET Web的 Razor Pages应用,配置热重载,解决.NET Core MVC 页面在更改后不刷新

Razor Pages应用&#xff0c;修改页面查看修改效果&#xff0c;如果没有热重载&#xff0c;改一句话跑一次&#xff0c;这个活就没法干了。 1、VS2022中的NuGet中安装RuntimeCompilation Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 需要配套你的.net sdk版本&#x…

DeepSeek(8):结合Kimi-PPT助手一键生成演示报告

1 生成内容 在Deepseek中生成内容&#xff1a; 帮我创建年度计划&#xff0c;描述《智能枕头》产品的如何在全国销售&#xff0c;计划切分到每个月。从而让我们的老板和团队对报告充满信息。输出的内容我需要放到ppt中进行展示。 使用Deepseek R1模型&#xff0c;如下&#x…

到底爱不爱我

L2-3 到底爱不爱我 古代少女有了心上人时&#xff0c;会悄悄折一条树枝&#xff0c;揪那枝上的叶子&#xff0c;揪一片叶子念一句“爱我”&#xff0c;再揪一片念一句“不爱我”…… 这样揪落最后一片叶子的时候&#xff0c;看看是停在“爱”还是“不爱”。 但聪明的慧娘一眼洞…

网络华为HCIA+HCIP 网络编程自动化

telnetlib介绍 telnetlib是Python标准库中的模块。它提供了实现Telnet功能的类telnetlib.Telnet。这里通过调用telnetlib.Telnet类里的不同方法实现不同功能。 配置云

【10】高效存储MongoDB的用法

目录 一、什么是MongoDB 二、准备工作 &#xff08;1&#xff09;安装MongoDB ​&#xff08;2&#xff09;安装pymongo库 三、连接MongoDB 四、指定数据库 五、指定集合 六、插入数据 &#xff08;1&#xff09; insert 方法 &#xff08;2&#xff09;insert_one(…

datawhale组队学习--大语言模型—task4:Transformer架构及详细配置

第五章 模型架构 在前述章节中已经对预训练数据的准备流程&#xff08;第 4 章&#xff09;进行了介绍。本章主 要讨论大语言模型的模型架构选择&#xff0c;主要围绕 Transformer 模型&#xff08;第 5.1 节&#xff09;、详细 配置&#xff08;第 5.2 节&#xff09;、主流架…