当前位置: 首页 > news >正文

【C++基础】多线程并发场景下的同步方法

如果在多线程程序中对全局变量的访问没有进行适当的同步控制(例如使用互斥锁、原子变量等),会导致多个线程同时访问和修改全局变量时发生竞态条件(race condition)。这种竞态条件可能会导致一系列不确定和严重的后果。
在C++中,可以通过使用互斥锁(mutex)、原子操作、读写锁来实现对全局变量的互斥访问。

一、缺乏同步控制造成的后果

1. 数据竞争(Data Race)

数据竞争发生在多个线程同时访问同一个变量,并且至少有一个线程在写该变量时没有进行同步。由于缺少同步机制,多个线程对全局变量的操作可能会相互干扰,导致变量的值不可预测。

示例:

#include <iostream>
#include <thread>int globalVar = 0;void increment() {globalVar++;
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Global variable: " << globalVar << std::endl;return 0;
}
后果:
  • 上面的代码中,globalVar++ 并不是一个原子操作。它由多个步骤组成:读取值、增加值、写回。在这段代码中,t1t2 可能会同时读取globalVar的值,导致两个线程同时修改它的值,最终的结果会小于预期的2。这就是典型的数据竞争。

2. 不一致的状态(Inconsistent State)

在没有同步控制的情况下,多个线程可能会对全局变量进行同时读写操作,导致变量处于不一致的状态。例如,多个线程可能会同时读取和修改相同的变量,导致最终状态不符合预期。

示例: 假设你有一个程序要求维护一个全局的计数器。如果没有加锁来确保线程安全,两个线程同时执行时,计数器可能会被写成一个无意义的值。

#include <iostream>
#include <thread>int counter = 0;void increment() {for (int i = 0; i < 100000; ++i) {counter++;  // 非线程安全操作}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Counter: " << counter << std::endl;return 0;
}
后果:
  • 在没有同步的情况下,counter++ 可能会导致多个线程在同一时刻读取到相同的计数器值,并同时将相同的更新值写回变量,这会使得counter的最终值远小于预期的200000
  • 这可能会导致程序的业务逻辑错误,特别是如果全局变量用作关键状态的标识。

3. 崩溃或程序未定义行为

由于数据竞争或者不一致的状态,程序可能会进入一个不可预测的状态,导致崩溃。全局变量的值在多线程的竞争中可能会发生损坏,从而导致未定义的行为(undefined behavior)。

例如:

  • 访问已释放内存:一个线程修改了全局变量并释放了相关内存,但其他线程仍然试图访问该内存。
  • 内存覆盖:多个线程同时修改全局变量,导致不同线程的操作互相覆盖,从而引发崩溃。

二、互斥锁std::mutex实现同步

std::mutex 是C++标准库中的一种机制,用于避免多个线程同时访问同一个资源(如全局变量)时发生竞争条件。

下面是一个示例,展示了如何使用std::mutex来保护全局变量:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;  // 定义全局互斥锁
int globalVar = 0;  // 定义全局变量void threadFunction() {std::lock_guard<std::mutex> lock(mtx);  // 上锁,确保互斥// 访问和修改全局变量++globalVar;std::cout << "Global variable: " << globalVar << std::endl;// 锁会在lock_guard离开作用域时自动释放
}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);t1.join();t2.join();return 0;
}

说明:

  1. std::mutex: 用于保护共享资源(如全局变量)。
  2. std::lock_guard<std::mutex>: 是一个RAII风格的封装器,它在构造时自动上锁,在析构时自动解锁,确保了线程安全。
  3. threadFunction中,每个线程在访问globalVar之前都会先获得互斥锁,这样就能确保线程之间不会同时访问和修改全局变量。

使用std::mutex可以防止不同线程之间因竞争访问全局变量而引发的错误或不一致问题。

有时如果你需要更细粒度的控制,还可以考虑使用std::unique_lock,它比std::lock_guard更灵活,允许手动控制锁的获取和释放。

三、独占锁std::unique_lock实现同步

std::unique_lock 是 C++11 标准库中的一种互斥锁包装器,它提供了比 std::lock_guard 更灵活的锁管理方式。std::unique_lock 允许手动控制锁的获取和释放,而不仅仅是在对象生命周期结束时自动释放锁(如 std::lock_guard 所做的那样)。这使得它比 std::lock_guard 更加灵活,适用于更复杂的场景,比如需要在同一作用域内多次锁定或解锁,或者需要在锁定期间进行一些其他操作。

std::unique_lock 的关键特性:

  1. 手动控制锁的获取和释放std::unique_lock 支持手动解锁和重新锁定,它比 std::lock_guard 更加灵活。
  2. 延迟锁定和提前解锁:你可以选择在对象创建时延迟锁定,或者在锁定后手动释放锁。
  3. 支持条件变量std::unique_lock 支持与条件变量一起使用,这是 std::lock_guard 无法做到的。

基本用法:

1. 构造时自动加锁

std::unique_lock 默认会在构造时自动加锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void threadFunction() {std::unique_lock<std::mutex> lock(mtx);  // 构造时自动上锁std::cout << "Thread is running\n";// 临界区的操作// 锁会在 lock 对象超出作用域时自动释放
}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);t1.join();t2.join();return 0;
}
2. 手动解锁与重新加锁

std::unique_lock 允许你在锁定期间手动解锁和重新加锁,这对于一些需要临时释放锁的场景非常有用。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void threadFunction() {std::unique_lock<std::mutex> lock(mtx);  // 构造时自动上锁std::cout << "Thread is running\n";// 临界区的操作lock.unlock();  // 手动解锁std::cout << "Lock released temporarily\n";// 临界区之外的操作lock.lock();  // 重新加锁std::cout << "Lock acquired again\n";// 临界区操作继续进行
}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);t1.join();t2.join();return 0;
}
3. 延迟锁定

std::unique_lock 也允许你延迟锁定,通过传递一个 std::defer_lock 参数给构造函数来实现。这会创建一个未锁定的 std::unique_lock,你可以在稍后手动调用 lock() 来加锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void threadFunction() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 延迟加锁std::cout << "Thread is preparing to run\n";// 做一些不需要加锁的操作lock.lock();  // 手动加锁std::cout << "Thread is running under lock\n";// 临界区的操作
}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);t1.join();t2.join();return 0;
}
4. 条件变量

std::unique_lock 是与条件变量一起使用的理想选择,它支持对互斥锁的手动解锁和重新加锁。这在条件变量的使用场景中非常有用,因为在等待条件时需要解锁互斥锁,而在条件满足时重新加锁。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void threadFunction() {std::unique_lock<std::mutex> lock(mtx);  // 上锁while (!ready) {  // 等待 ready 为 truecv.wait(lock);  // 等待,自动解锁并挂起线程}std::cout << "Thread is running\n";
}void notify() {std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟一些操作std::cout << "Notifying the threads\n";std::unique_lock<std::mutex> lock(mtx);  // 上锁ready = true;cv.notify_all();  // 通知所有线程
}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);std::thread notifier(notify);t1.join();t2.join();notifier.join();return 0;
}

解释:

  1. std::condition_variablestd::unique_lock

    • threadFunction 中,cv.wait(lock) 会释放锁并等待条件变量的通知。
    • std::unique_lock 能够在调用 wait 时自动释放锁,并且在 wait 返回时会重新加锁,这使得 std::unique_lock 成为使用条件变量的最佳选择。
  2. cv.notify_all():通知所有等待该条件的线程,thread1thread2 都会在条件满足时继续执行。

四、共享锁std::shared_mutex实现同步

std::shared_mutex 是 C++17 引入的一个同步原语,它提供了一种读写锁机制,允许多个线程共享读取同一资源,而只有一个线程能够独占写入该资源。相比于传统的 std::mutex(只支持独占锁),std::shared_mutex 可以提高并发性,特别是在读操作远多于写操作的情况下。

std::shared_mutex 的工作原理:

  • 共享锁(shared lock):多个线程可以同时获取共享锁,这意味着多个线程可以同时读取共享资源。多个线程获取共享锁时不会发生冲突。
  • 独占锁(unique lock):只有一个线程可以获取独占锁,这意味着写操作会阻塞其他所有操作(无论是读操作还是写操作),以保证数据的一致性。

使用 std::shared_mutex

std::shared_mutex 提供了两种类型的锁:

  • std::unique_lock<std::shared_mutex>:用于获取独占锁。
  • std::shared_lock<std::shared_mutex>:用于获取共享锁。

1. 基本使用示例

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>std::shared_mutex mtx;  // 定义一个 shared_mutex
int sharedData = 0;void readData(int threadId) {std::shared_lock<std::shared_mutex> lock(mtx);  // 获取共享锁std::cout << "Thread " << threadId << " is reading data: " << sharedData << std::endl;
}void writeData(int threadId, int value) {std::unique_lock<std::shared_mutex> lock(mtx);  // 获取独占锁sharedData = value;std::cout << "Thread " << threadId << " is writing data: " << sharedData << std::endl;
}int main() {std::vector<std::thread> threads;// 启动多个线程进行读取操作for (int i = 0; i < 5; ++i) {threads.push_back(std::thread(readData, i));}// 启动一个线程进行写入操作threads.push_back(std::thread(writeData, 100, 42));// 等待所有线程结束for (auto& t : threads) {t.join();}return 0;
}

解释:

  1. 共享锁 (std::shared_lock):线程 readData 使用 std::shared_lock 获取共享锁,这允许多个线程同时读取 sharedData,因为读取操作是线程安全的。
  2. 独占锁 (std::unique_lock):线程 writeData 使用 std::unique_lock 获取独占锁,这确保了只有一个线程可以写 sharedData,并且写操作会阻塞所有其他线程(包括读操作和写操作)。

2. 多个读线程与单个写线程的并发控制

在这个示例中,多个读线程可以并行执行,因为它们都获取了共享锁。只有当写线程(获取独占锁)执行时,其他线程(无论是读线程还是写线程)会被阻塞。

  • 写操作:获取独占锁,所有读操作和写操作都会被阻塞,直到写操作完成。
  • 读操作:多个线程可以同时获取共享锁,只有在没有写操作时才会执行。

3. 共享锁与独占锁的冲突

  • 共享锁:多个线程可以同时获取共享锁,只要没有线程持有独占锁。共享锁不会阻塞其他共享锁请求。
  • 独占锁:当一个线程持有独占锁时,其他任何线程的共享锁或独占锁请求都会被阻塞,直到独占锁释放。

4. 使用场景

std::shared_mutex 主要适用于读多写少的场景。假设有一个资源(如缓存、数据结构),它在大部分时间内被多个线程读取,但偶尔需要被更新。在这种情况下,std::shared_mutex 可以让多个读操作并行执行,同时避免写操作导致的不必要的阻塞。

例如:

  • 缓存数据读取:多个线程可以并发读取缓存中的数据,而当缓存需要更新时,独占锁会确保数据一致性。
  • 数据库的并发查询和修改:多个线程可以并发查询数据库,但只有一个线程可以执行写操作。

5. std::shared_mutexstd::mutex 比较

  • std::mutex:提供独占锁,适用于写操作频繁且不需要并发读的场景。每次加锁时,其他线程都无法进入临界区。
  • std::shared_mutex:适用于读多写少的场景,允许多个线程同时读取共享资源,但写操作会阻塞所有其他操作。

6. 性能考虑

  • 读操作频繁时:使用 std::shared_mutex 可以提高并发性,因为多个线程可以同时读取数据。
  • 写操作频繁时:性能可能会低于 std::mutex,因为写操作需要独占资源并阻塞所有其他操作。

7. 条件变量

std::mutex 一样,std::shared_mutex 也可以与条件变量(std::condition_variable)一起使用,不过在使用时要注意,不同的线程需要加锁和解锁对应的锁。

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <condition_variable>std::shared_mutex mtx;
std::condition_variable_any cv;
int sharedData = 0;void readData() {std::shared_lock<std::shared_mutex> lock(mtx);  // 获取共享锁while (sharedData == 0) {  // 等待数据可用cv.wait(lock);  // 等待数据被写入}std::cout << "Reading data: " << sharedData << std::endl;
}void writeData(int value) {std::unique_lock<std::shared_mutex> lock(mtx);  // 获取独占锁sharedData = value;std::cout << "Writing data: " << sharedData << std::endl;cv.notify_all();  // 通知所有等待的线程
}int main() {std::thread reader(readData);std::thread writer(writeData, 42);reader.join();writer.join();return 0;
}

解释:

  • std::shared_lock:用于共享读锁,允许多个线程同时读取。
  • cv.wait(lock):使用共享锁来等待某些条件的变化。
  • cv.notify_all():通知所有等待线程,唤醒它们继续执行。

五、std::atomic实现同步

std::atomic 是 C++11 标准引入的一种类型,用于实现原子操作。原子操作指的是操作在执行过程中不可被中断,因此能够保证数据的一致性和正确性。

std::atomic 提供了一些基本的原子操作方法,这些操作是不可分割的,保证了在多线程环境下线程安全。它主要用于数据的同步与协作,避免了传统同步原语(如锁、条件变量)所带来的性能瓶颈。

原子操作的基本概念:

  1. 原子性:在执行时,操作不能被打断,保证线程之间对共享变量的操作不会产生竞态条件。
  2. 内存顺序(Memory Ordering):控制操作的执行顺序和对共享数据的可见性,std::atomic 允许通过内存顺序来显式指定不同线程间的同步行为。

std::atomic 提供的原子操作:

  1. 加载(Load):从原子变量中读取数据。
  2. 存储(Store):将数据存储到原子变量中。

std::atomic 支持的内存顺序(Memory Ordering):

  • std::memory_order_acquire:确保前面的操作在加载之后执行,即它会阻止后续的操作在此之前执行。
  • std::memory_order_release:确保后面的操作在存储之前执行,即它会阻止前面的操作在此之后执行。

通常情况下,在使用 std::atomic 进行同步时,使用 memory_order_releasestore 操作时,使用 memory_order_acquireload 操作时,是一种常见的模式,特别是在生产者-消费者模式或者其他类似的同步模式下。

memory_order_releasememory_order_acquire 一般搭配使用。

这种组合是为了确保 内存顺序的一致性,并且保证数据正确的可见性。具体来说:

  1. memory_order_release:在执行 store 操作时,它会确保在 store 之前的所有操作(如数据写入)不会被重排序到 store 之后,保证当前线程的写操作对其他线程是可见的。因此,store 操作保证所有前置的写操作都会在这个 store 完成后被其他线程看到。

  2. memory_order_acquire:在执行 load 操作时,它会确保在 load 之后的所有操作(如数据读取)不会被重排序到 load 之前,保证当前线程在读取共享数据后,后续的操作可以看到正确的数据。在 load 之前的所有操作(包括对共享变量的写入)会在读取这个值之后对当前线程可见。

这两者配合使用,确保了线程间的同步,避免了数据竞态条件。

具体场景

考虑一个生产者-消费者模型,生产者负责写入数据并通知消费者,消费者负责读取数据并处理。

示例:
#include <iostream>
#include <atomic>
#include <thread>std::atomic<int> data(0);
std::atomic<bool> ready(false);void consumer() {while (!ready.load(std::memory_order_acquire)) {// 等待 ready 为 true}std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl;
}void producer() {data.store(42, std::memory_order_relaxed);  // 写数据ready.store(true, std::memory_order_release);  // 设置 ready 为 true
}int main() {std::thread t1(consumer);std::thread t2(producer);t1.join();t2.join();return 0;
}

解释:

  1. ready.store(true, std::memory_order_release):生产者线程在写入 ready 时使用 memory_order_release,这意味着在 ready 设置为 true 之后,所有在此之前的操作(如对 data 的写入)对消费者线程是可见的。

  2. ready.load(std::memory_order_acquire):消费者线程在读取 ready 时使用 memory_order_acquire,这意味着消费者线程在读取 ready 后,确保它能够看到生产者线程在 store ready 之前所做的所有修改(如 data 的值)。

这种组合保证了生产者线程的写操作(例如 data.store(42))对于消费者线程是可见的,且在读取 ready 后,消费者线程可以安全地读取到更新后的 data

相关文章:

【C++基础】多线程并发场景下的同步方法

如果在多线程程序中对全局变量的访问没有进行适当的同步控制&#xff08;例如使用互斥锁、原子变量等&#xff09;&#xff0c;会导致多个线程同时访问和修改全局变量时发生竞态条件&#xff08;race condition&#xff09;。这种竞态条件可能会导致一系列不确定和严重的后果。…...

【优选算法】10----无重复字符的最长子串

---------------------------------------begin--------------------------------------- 题目解析&#xff1a; 看到这一类题目&#xff0c;有没有那种一眼就感觉时要用到滑动窗口的感觉&#xff0c;铁子们&#xff1f; 讲解算法原理&#xff1a; 方法一: 暴力解法&#xff…...

C语言程序设计十大排序—冒泡排序

文章目录 1.概念✅2.冒泡排序&#x1f388;3.代码实现✅3.1 直接写✨3.2 函数✨ 4.总结✅ 1.概念✅ 排序是数据处理的基本操作之一&#xff0c;每次算法竞赛都很多题目用到排序。排序算法是计算机科学中基础且常用的算法&#xff0c;排序后的数据更易于处理和查找。在计算机发展…...

26考研资料分享 百度网盘

基础班&#xff1a; 通过网盘分享的文件&#xff1a;2026【考研数学】等3个文件 链接:https://pan.baidu.com/s/1djzJiut1h0DH8WmrI05YHg?pwd1234 提取码:1234--来自百度网盘超级会员v3的分享 通过网盘分享的文件&#xff1a;01、2026【考研政治】 链接:https://pan.baidu.…...

C++ lambda表达式

目录 1.lambda表达式 1.1什么是Lambda表达式&#xff1f; 1.2Lambda表达式的语法 1.3捕捉列表 1.4函数对象与lambda表达式 1.lambda表达式 1.1什么是Lambda表达式&#xff1f; Lambda表达式是C11标准引入的一种匿名函数&#xff0c;它允许你在需要函数的地方直接编写代码…...

halo附件图片迁移到easyimage图床

前言 在使用 markdown 编写文章之后&#xff0c;markdown 内容中引用的图片都是图床的地址 部分文章是在 halo 没有使用图床前&#xff0c;就已经发布了的&#xff0c;这些图片都是直接存储在 halo 系统中的 需要将这些文章的图片全部迁移到图床上&#xff0c;这些文章的图片…...

Grafana

Grafana 是一款广泛使用的开源数据可视化与监控平台&#xff0c;通过与 Prometheus、InfluxDB、Elasticsearch 等多种数据源集成&#xff0c;提供强大的监控、数据展示和告警功能。其告警体系在及时通知系统异常、保障业务稳定性方面起到关键作用。 Grafana 告警体系概述 Graf…...

前端性能优化:HMR热更新和预获取加载

最近发现项目开发&#xff0c;有点加载快&#xff0c;有点却是卡机式&#xff0c;甚至刷新导致白屏情况。于是&#xff0c;我找开发和性能优化的方法&#xff0c;找到下面几种。 本文将深入探讨 预获取&#xff08;Prefetch&#xff09;、动态导入&#xff08;Dynamic Import&…...

OpenCV相机标定与3D重建(66)对立体匹配生成的视差图(disparity map)进行验证的函数validateDisparity()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用左右检查来验证视差。矩阵 “cost” 应该由立体对应算法计算。 cv::validateDisparity 函数是 OpenCV 库中用于对立体匹配生成的视差图&…...

git reset (取消暂存,保留工作区修改)

出现这种情况的背景&#xff1a;我不小心把node_modules文件添加到暂存区了&#xff0c;由于文件过大&#xff0c;导致不能提交&#xff0c;所以我想恢复之前的状态&#xff0c;但又不想把修改的代码恢复为之前的状态&#xff0c;所以使用这个命令可以只恢复暂存区的状态&#…...

【论文+源码】diffuseq使用扩散模型和diffuseq-v2的序列文本生成序列,并且桥接离散和连续的文本空间,用于加速SEQ2SEQ扩散模型。

这篇论文介绍了一种名为DIFFUSEQ的新型扩散模型&#xff0c;专门针对序列到序列&#xff08;SEQ2SEQ&#xff09;文本生成任务进行设计。尽管扩散模型在视觉和音频等连续信号领域取得了成功&#xff0c;但在自然语言处理特别是条件生成方面的适应仍然未被广泛探索。通过广泛的评…...

考研机试题:打印日期

描述 给出年分m和一年中的第n天&#xff0c;算出第n天是几月几号。 输入描述: 输入包括两个整数y(1<y<3000)&#xff0c;n(1<n<366)。 输出描述: 可能有多组测试数据&#xff0c;对于每组数据&#xff0c; 按 yyyy-mm-dd的格式将输入中对应的日期打印出来。 …...

Ubuntu 20.04 x64下 编译安装ffmpeg

试验的ffmpeg版本 4.1.3 本文使用的config命令 ./configure --prefixhost --enable-shared --disable-static --disable-doc --enable-postproc --enable-gpl --enable-swscale --enable-nonfree --enable-libfdk-aac --enable-decoderh264 --enable-libx265 --enable-libx…...

springboot使用ssl连接elasticsearch

使用es时ssl证书报错 unable to find valid certification path to requested target 1.依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>2…...

虚拟头节点和双指针解决链表问题(合并,与分解操作,力扣题目为例)

Problem: 21. 合并两个有序链表 Problem: 86. 分隔链表 文章目录 总览说明题目描述思路复杂度Code总结分析 总览说明 在解决链表相关的算法题目时较多使用到的技巧就是虚拟头节点、双指针&#xff0c;而题目往往都会涉及到对链表的分解、合并操作&#xff0c;本文选择两个题目将…...

JavaScript系列(38)-- WebRTC技术详解

JavaScript WebRTC技术详解 &#x1f3a5; 今天&#xff0c;让我们深入了解WebRTC&#xff08;Web Real-Time Communication&#xff09;技术&#xff0c;这是一种支持网页浏览器进行实时语音对话或视频对话的技术。 WebRTC基础概念 &#x1f31f; &#x1f4a1; 小知识&…...

分布式理解

分布式 如何理解分布式 狭义的分布是指&#xff0c;指多台PC在地理位置上分布在不同的地方。 分布式系统 分布式系**统&#xff1a;**多个能独立运行的计算机&#xff08;称为结点&#xff09;组成。各个结点利用计算机网络进行信息传递&#xff0c;从而实现共同的“目标或者任…...

JVM学习指南(48)-JVM即时编译

文章目录 即时编译(Just-In-Time Compilation, JIT)概述为什么JVM需要即时编译?即时编译与传统的静态编译的区别JVM中的即时编译器HotSpot VM中的C1和C2编译器编译器的作用和位置即时编译的工作流程代码的加载和解释执行热点代码检测编译优化编译优化技术公共子表达式消除循…...

python http调用视觉模型moondream

目录 一、什么是moondream 二、资源地址 三、封装了http进行接口请求 四、代码解析 解释 可能的改进 一、什么是moondream Moondream 是一个针对视觉生成任务的深度学习模型,专注于图像理解和生成,包括图像标注(captioning)、问题回答(Visual Question Answering,…...

【竞技宝】DOTA2:NAVI junior被ESL取消参赛资格

北京时间1月24日,DOTA2目前有多个赛事的预选赛正在如火如荼的进行之中,其中ESLOne罗利站的预选赛已经结束,参赛正赛的队伍也已经全部产生。除了因EPT积分而拿到直邀资格的PARI、BB、falcons、liquid之外,tundra、NAVI junior、spirit、nigma、XG、talon、SR、HEROIC也通过各赛区…...

Java 在包管理与模块化中的优势:与其他开发语言的比较

在开发复杂的、规模庞大的软件系统时&#xff0c;包管理和模块化设计起着至关重要的作用。它们不仅决定了代码的组织和可维护性&#xff0c;还直接影响到团队协作效率、扩展性和性能。在众多编程语言中&#xff0c;Java 凭借其成熟的生态系统、强类型系统和标准化的包管理机制&…...

如何用数据编织、数据虚拟化与SQL-on-Hadoop打造实时、可扩展兼容的数据仓库?

在大数据技术迅猛发展的背景下&#xff0c;许多人认为传统数据仓库已过时。然而&#xff0c;这种观点忽略了数据仓库的核心价值&#xff1a;统一的数据视图、强大的业务逻辑支撑以及丰富的数据分析能力。在企业数据架构转型中&#xff0c;数据仓库不仅未被淘汰&#xff0c;反而…...

MVCC底层原理实现

MVCC的实现原理 了解实现原理之前&#xff0c;先理解下面几个组件的内容 1、 当前读和快照读 先普及一下什么是当前读和快照读。 当前读&#xff1a;读取数据的最新版本&#xff0c;并对数据进行加锁。 例如&#xff1a;insert、update、delete、select for update、 sele…...

【Nacos】负载均衡

目录 前言 一、服务下线二、权重配置三、同一个集群优先访问四、环境隔离 前言 我们的生产环境相对是比较恶劣的&#xff0c;我们需要对服务的流量进行更加精细的控制.Nacos支持多种负载均衡策略&#xff0c;包括配置权重&#xff0c;同机房&#xff0c;同地域&#xff0c;同环…...

Batch Normalization学习笔记

文章目录 一、为何引入 Batch Normalization二、具体步骤1、训练阶段2、预测阶段 三、关键代码实现四、补充五、参考文献 一、为何引入 Batch Normalization 现在主流的卷积神经网络几乎都使用了批量归一化&#xff08;Batch Normalization&#xff0c;BN&#xff09;1&#xf…...

PIC单片机HEX文件格式分析

在调试PIC单片机在bootloader程序时&#xff0c;需要将hex文件转换为bin文件&#xff0c;在转换之前先了解一下hex文件中数据是如何定义的。 直接打开一个LED灯闪烁的程序生成的hex文件&#xff0c;芯片型号为PIC18F46K80 可以看到每条数据都是由6部分组成的&#xff0c;下面分…...

【unity游戏开发之InputSystem——07】InputSystem+UGUI配合使用(基于unity6开发介绍)

文章目录 一、InputSystem+UGUI配合使用1、官方文档参考2、切换到新的输入模块二、UGUI中的新输入系统输入模块参数相关1、Send Pointer Hover To Parent2、Move Repeat Delay3、Move Repeat Rate4、XR Tracking Origin5、Deselect On Background CLick6、Pointer Behavior7、S…...

RocketMQ 的 Topic 和消息队列MessageQueue信息,是怎么分布到Broker的?怎么负载均衡到Broker的?

目录 1. Topic 和 MessageQueue 的基本概念 1.1 Topic 1.2 MessageQueue 2. Topic 和 MessageQueue 的分布 2.1 Topic 的创建 2.2 MessageQueue 分配到 Broker 2.3 分布规则 3. 负载均衡机制 3.1 Producer 的负载均衡 3.2 Consumer 的负载均衡 3.3 Broker 的负载均衡…...

Qt简单迷宫游戏

目录 你将学到你将准备你将改变你将设计你将编程开始界面游玩界面胜利界面其它bug修复 你可扩展下一篇博客要说的东西 你将学到 Qt中QKeySequence对象的基本创建Qt中QShortcut对象的基本应用Qt中QSoundEffect对象的基本应用 你将准备 在开始制作Qt简单迷宫游戏之前&#xff…...

Webrtc (1) - Windows 编译

最近项目上遇到webrtc wgc 的几个test case无法通过&#xff0c;与webrtc人员沟通后决定要自行修复一下(因为他们不想管…) 参考文档 https://webrtc.org/support/contributinghttps://chromium.googlesource.com/chromium/src//main/docs/#checking-out-and-building 以上两…...

深圳大学-智能网络与计算-实验一:RFID原理与读写操作

实验目的与要求 掌握超高频RFID标签的寻卡操作。掌握超高频RFID标签的读写操作。掌握超高频RFID标签多张卡读取时的防冲突机制。 方法&#xff0c;步骤 软硬件的连接与设置超高频RFID寻卡操作超高频RFID防冲突机制超高频RFID读写卡操作 实验过程及内容 一&#xff0e;软硬…...

文献引用指南ChatGPT提示词分享

文献引用指南 在学术写作中&#xff0c;准确引用是至关重要的环节。它不仅能够为您的研究提供坚实的学术基础&#xff0c;还能确保您尊重并认可他人的学术成果&#xff0c;从而有效避免抄袭的问题。而ChatGPT在这一方面同样能够为您提供有力的支持。借助ChatGPT&#xff0c;您…...

什么是计算机应用基础知识

计算机应用基础知识是指学习和掌握计算机的基本理论、硬件、软件、网络和应用技能的基础内容。它是为使用计算机进行日常工作、学习和解决实际问题打下的基础。计算机应用基础知识涉及多个领域,主要包括以下几个方面: 一、计算机硬件基础 计算机硬件组成:了解计算机的基本组…...

SpringBoot集成Flink-CDC,实现对数据库数据的监听

一、什么是 CDC &#xff1f; CDC 是Change Data Capture&#xff08;变更数据获取&#xff09;的简称。 核心思想是&#xff0c;监测并捕获数据库的变动&#xff08;包括数据或数据表的插入、 更新以及删除等&#xff09;&#xff0c;将这些变更按发生的顺序完整记录下来&…...

微信小程序云开发服务端存储API 从云存储空间删除文件

deleteFile 从云存储空间删除文件 const cloud require(wx-server-sdk)exports.main async (event, context) > {const fileIDs [xxx, xxx]const result await cloud.deleteFile({fileList: fileIDs,})return result.fileList }写的资式如有不对&#xff0c;请各位大神多…...

[操作系统] 深入进程地址空间

程序地址空间回顾 在C语言学习的时&#xff0c;对程序的函数、变量、代码等数据的存储有一个大致的轮廓。在语言层面上存储的地方叫做程序地址空间&#xff0c;不同类型的数据有着不同的存储地址。 下图为程序地址空间的存储分布和和特性&#xff1a; 使用以下代码来验证一下…...

gitlab处理空文件夹的提交或空文件夹提交失败

问题描述 厂家发给了我一个压缩包文件&#xff0c;压缩包解压之后本地编译没问题&#xff1b;推送到gitlab之后&#xff0c;再编译就报错了&#xff1b; 问题原因 经过分析之后发现&#xff0c;压缩包解压之后存在很多空文件夹&#xff1b;但是gitlab推送的时候&#xff0c;…...

C++ —— 智能指针 unique_ptr (上)

C —— 智能指针 unique_ptr &#xff08;上&#xff09; 普通指针的不足普通指针的释放智能指针智能指针 unique_ptr智能指针初始化错误用法get()方法返回裸指针智能指针不支持指针的运算&#xff08;、-、、- -&#xff09; 普通指针的不足 new和new [] 的内存需要用delete和…...

ruoyi-vue-pro集成magic-api(图文代码)

目录 前言1. 配置依赖2. 集成登录3. 成功展示前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 原先写过简单的集成:了解 magic-api的基本用法 附实战代码 magic-api相关文档推荐阅读:接口鉴权 相关的Java代码推荐阅读: java框架 零基础从入门到精…...

【线上问题定位处理】及【性能优化】系列文章

目录 性能优化 性能优化 九大服务架构性能优化方式 如何进行GC调优 如何排查线上系统出现的Full GC MySQL - 性能优化 MySQL - 分库分表 大数据查询的处理方案 MySQL优化手段有哪些 服务CPU100%问题如何快速定位? 服务内存OOM问题如何快速定位? JVM调优6大步骤 线…...

如何解压rar格式文件?8种方法(Win/Mac/手机/网页端)

RAR 文件是一种常见的压缩文件格式&#xff0c;由尤金・罗谢尔&#xff08;Eugene Roshal&#xff09;开发&#xff0c;因其扩展名 “rar” 而得名。它通过特定算法将一个或多个文件、文件夹进行压缩&#xff0c;大幅减小存储空间&#xff0c;方便数据传输与备份。然而&#xf…...

GORM 支持的数据库解析

GORM 是一个流行的 Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它简化了 Go 与数据库之间的交互。作为一款功能强大的库&#xff0c;GORM 支持多种主流关系型数据库&#xff0c;满足不同开发场景下的需求。本文将探讨 GORM 支持的主要数据库及其特点&#…...

分布式光纤应变监测是一种高精度、分布式的监测技术

一、土木工程领域 桥梁结构健康监测 主跨应变监测&#xff1a;在大跨度桥梁的主跨部分&#xff0c;如悬索桥的主缆、斜拉桥的斜拉索和主梁&#xff0c;分布式光纤应变传感器可以沿着这些关键结构部件进行铺设。通过实时监测应变情况&#xff0c;能够精确捕捉到车辆荷载、风荷…...

如何在oracle关闭情况下如何修改spfile的参数

如何在oracle关闭情况下如何修改spfile的参数 一、问题背景二、处理方案 一、问题背景 在ORACLE数据库启动状态下通过下列代码修改了最大连接数 alter system set processes2000 scopespfile sid*;关闭了数据库再重新启动提示如下报错&#xff1a; ORA-00838: Specified valu…...

[ Spring ] Spring Cloud Gateway 2025 Comprehensive Overview

文章目录 Spring Gateway ArchitectureProject Level DependencyService CenterService ProviderGateway ServiceLaunch All Service Spring Gateway Architecture Service Center : register and find service providerService Provider : programs that provide actual serv…...

软考信安27~Windows操作系统安全相关

1、Windows账户与组管理 1.1、用户账户查看 whoami #查看当前登录的用户名称 whoami /all #查看当前系统的用户名和组信息,以及SID whoami /user #查看当前用户的SID net user #查看系统中包含哪些用户 wmic useraccount get name,sid #查看…...

2025年数学建模美赛 A题分析(3)楼梯使用方向偏好模型

2025年数学建模美赛 A题分析&#xff08;1&#xff09;Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析&#xff08;2&#xff09;楼梯磨损分析模型 2025年数学建模美赛 A题分析&#xff08;3&#xff09;楼梯使用方向偏好模型 2025年数学建模美赛 A题分…...

Spring整合Mybatis、junit纯注解

如何创建一个Spring项目 错误问题 不知道什么原因&#xff0c;大概是依赖版本不兼容、java版本不对的问题&#xff0c;折磨了好久就是搞不成。 主要原因看pom.xml配置 pom.xml配置 java版本 由于是跟着22年黑马视频做的&#xff0c;java版本换成了jdk-11&#xff0c;用21以…...

HAM-TTS大模型:基于token的零样本文字转语音分层声学建模

HAM-TTS大模型:基于token的零样本文字转语音分层声学建模 ​​ 吉利自研语音大模型HAM-TTS的全称是:Hierarchical Acoustic Modeling for Token-Based Zero-Shot Text-to-Speech,直译是基于token的零样本文字转语音分层声学建模,是星睿AI大模型体系下的重要一员。顾名思义…...

SpringBoot开发(三)SpringBoot介绍、项目创建、运行

1. SpringBoot 1.1. SpringBoot介绍 Spring Boot给世界程序员带来了春天&#xff0c;越来越多的企业选择使用spring boot来开发他们的软件&#xff0c;因此学习spring boot是科技发展的必然趋势。本门课程将从web最基础的知识点开始讲起&#xff0c;逐步带你攻破spring boot的…...