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

C++编程:无锁环形队列 (LockFreeRingQueue)的简单实现、测试和分析

文章目录

    • 0. 概述
    • 1. 无锁环形队列概述
      • 1.1 无锁环形队列的特点
      • 1.2 无锁环形队列的优势与局限
    • 2. `LockFreeRingQueue` 实现
      • 2.1 `Enqueue` 操作流程图
      • 2.2 `Dequeue` 操作流程图
    • 3. 核心实现细节
      • 3.1 环形队列的大小调整
      • 3.2 索引计算
      • 3.3 原子操作与CAS
      • 3.4 线程让步
    • 4. 测试示例程序
      • 4.1 实现代码
      • 4.2 代码解释:
      • 4.3 运行程序:
      • 4.4 执行结果:
    • 5. 单元测试
      • 5.1 测试代码解释:
      • 5.2 测试运行:
    • 6. 总结

0. 概述

在现代多线程编程中,高效的并发数据结构对于提升系统性能至关重要,尤其是在处理高并发场景时。本文将详细介绍一种无锁环形队列 (LockFreeRingQueue) 的实现,并探讨其在实际应用中的优势与局限。
本文详细测试代码 lock_free_ring_queue

1. 无锁环形队列概述

无锁环形队列是一种高效的并发数据结构,适用于多线程环境下的生产者-消费者模型。它通过使用原子操作(如CAS操作)来避免锁的使用,从而消除了锁竞争带来的性能开销和潜在的死锁风险。

1.1 无锁环形队列的特点

  1. 线程安全:通过原子操作保证数据的正确性。
  2. 高效性:避免了锁竞争,减少了线程上下文切换的开销。
  3. 避免ABA问题:设计时特别考虑了ABA问题的影响,通过合理的索引管理避免了这一问题。

1.2 无锁环形队列的优势与局限

优势

  1. 高并发性:无锁结构通过避免锁的使用,使多个线程可以并发执行,提高了吞吐量。
  2. 低延迟:在高并发场景下,无锁结构减少了线程竞争带来的上下文切换,降低了系统延迟。
  3. 避免死锁:由于没有锁的存在,自然避免了死锁问题。

局限

  1. 复杂性:无锁结构通常比锁机制实现更复杂,容易引入难以调试的并发错误。
  2. 硬件依赖性:原子操作(如CAS)通常依赖于底层硬件支持,在不同平台上表现可能有所不同。
  3. 有限应用场景:无锁队列并不适合所有场景,在某些情况下(如低并发或非实时系统),传统的锁机制可能更为适合。

2. LockFreeRingQueue 实现

下面是一个基于C++实现的无锁环形队列的实现,该队列支持多生产者和多消费者线程的并发访问。

#ifndef RING_QUEUE_HPP
#define RING_QUEUE_HPP#include <atomic>
#include <memory>
#include <stdexcept>
#include <thread>template <typename T>
class LockFreeRingQueue {public:// Constructor that initializes the ring queue with the specified sizeexplicit LockFreeRingQueue(uint32_t size);// Default destructor~LockFreeRingQueue() = default;// Disable copy constructor and assignment operatorLockFreeRingQueue(const LockFreeRingQueue&) = delete;LockFreeRingQueue& operator=(const LockFreeRingQueue&) = delete;// Enqueue operation to add an element to the queuebool Enqueue(const T& data);// Dequeue operation to remove an element from the queuebool Dequeue(T* data);// Check if the queue is emptybool IsEmpty() const noexcept;// Check if the queue is fullbool IsFull() const noexcept;// Get the current size of the queueuint32_t Size() const noexcept;private:// Check if the given number is a power of twostatic bool IsPowerOfTwo(uint32_t num) noexcept;// Calculate the ceiling power of two greater than or equal to the given numberstatic uint32_t CeilPowerOfTwo(uint32_t num) noexcept;// Round up the given number to the nearest power of twostatic uint32_t RoundupPowerOfTwo(uint32_t num) noexcept;// Get the index within the queueuint32_t IndexOfQueue(uint32_t index) const noexcept;private:const uint32_t size_;                     // Size of the queue, must be a power of twostd::atomic<uint32_t> length_;            // Current length of the queuestd::atomic<uint32_t> read_index_;        // Index for the consumer to readstd::atomic<uint32_t> write_index_;       // Index for the producer to writestd::atomic<uint32_t> last_write_index_;  // Last confirmed write indexstd::unique_ptr<T[]> queue_;              // Array to store the queue elements
};template <typename T>
LockFreeRingQueue<T>::LockFreeRingQueue(uint32_t size): size_(size <= 1U           ? 2U: IsPowerOfTwo(size) ? size: RoundupPowerOfTwo(size)),length_(0U),read_index_(0U),write_index_(0U),last_write_index_(0U),queue_(std::make_unique<T[]>(size_)) {if (size == 0U) {throw std::out_of_range("Queue size must be greater than 0");}
}template <typename T>
bool LockFreeRingQueue<T>::Enqueue(const T& data) {uint32_t current_read_index;uint32_t current_write_index;do {current_read_index = read_index_.load(std::memory_order_relaxed);current_write_index = write_index_.load(std::memory_order_relaxed);// Check if the queue is fullif (IndexOfQueue(current_write_index + 1U) == IndexOfQueue(current_read_index)) {return false;  // Queue is full}} while (!write_index_.compare_exchange_weak(current_write_index, current_write_index + 1U, std::memory_order_release,std::memory_order_relaxed));queue_[IndexOfQueue(current_write_index)] = data;// Confirm the write operationwhile (!last_write_index_.compare_exchange_weak(current_write_index, current_write_index + 1U,std::memory_order_release, std::memory_order_relaxed)) {std::this_thread::yield();  // Yield CPU to avoid busy-waiting}length_.fetch_add(1U, std::memory_order_relaxed);return true;
}template <typename T>
bool LockFreeRingQueue<T>::Dequeue(T* data) {if (data == nullptr) {throw std::invalid_argument("Null pointer passed to Dequeue");}uint32_t current_read_index;uint32_t current_last_write_index;do {current_read_index = read_index_.load(std::memory_order_relaxed);current_last_write_index = last_write_index_.load(std::memory_order_relaxed);// Check if the queue is emptyif (IndexOfQueue(current_last_write_index) == IndexOfQueue(current_read_index)) {return false;  // Queue is empty}*data = queue_[IndexOfQueue(current_read_index)];if (read_index_.compare_exchange_weak(current_read_index, current_read_index + 1U, std::memory_order_release,std::memory_order_relaxed)) {length_.fetch_sub(1U, std::memory_order_relaxed);return true;}} while (true);
}template <typename T>
bool LockFreeRingQueue<T>::IsEmpty() const noexcept {return length_.load(std::memory_order_relaxed) == 0U;
}template <typename T>
bool LockFreeRingQueue<T>::IsFull() const noexcept {uint32_t next_write_index = IndexOfQueue(write_index_.load(std::memory_order_relaxed) + 1U);return next_write_index == read_index_.load(std::memory_order_acquire);
}template <typename T>
uint32_t LockFreeRingQueue<T>::Size() const noexcept {return length_.load(std::memory_order_relaxed);
}template <typename T>
bool LockFreeRingQueue<T>::IsPowerOfTwo(uint32_t num) noexcept {return (num != 0U) && ((num & (num - 1U)) == 0U);
}template <typename T>
uint32_t LockFreeRingQueue<T>::CeilPowerOfTwo(uint32_t num) noexcept {num |= (num >> 1U);num |= (num >> 2U);num |= (num >> 4U);num |= (num >> 8U);num |= (num >> 16U);return num - (num >> 1U);
}template <typename T>
uint32_t LockFreeRingQueue<T>::RoundupPowerOfTwo(uint32_t num) noexcept {return CeilPowerOfTwo((num - 1U) << 1U);
}template <typename T>
uint32_t LockFreeRingQueue<T>::IndexOfQueue(uint32_t index) const noexcept {return index & (size_ - 1U);
}#endif  // RING_QUEUE_HPP

2.1 Enqueue 操作流程图

Full
Not Full
Success
Fail
Success
Fail
Start enQueue
Check if Full
Return false
CAS Update Write Index
Write Data
CAS Update Last Write Index
Increment Length
End enQueue
Yield Thread

2.2 Dequeue 操作流程图

Empty
Not Empty
Success
Fail
Start deQueue
Check if Empty
Return false
Read Data from Queue
CAS Update Read Index
Decrement Length
End deQueue

3. 核心实现细节

3.1 环形队列的大小调整

环形队列的大小通常为2的幂次,以便可以通过位运算快速计算索引。RoundUpPowerOfTwo 函数将任意正整数向上调整为最接近的2的幂次。

确保环形队列的大小是 2 的幂次方主要有以下几个原因:

  • 简化索引计算:

    • 在环形队列中,元素的位置通过取模运算来计算。
    • 如果队列的大小是 2 的幂次方,那么取模运算可以被位运算 & 替代,这个操作更加高效。
  • 避免对齐问题:

    • 对于某些硬件架构来说,内存访问的效率会受到内存对齐的影响。
    • 如果队列大小是 2 的幂次方,那么它就能很好地符合内存对齐要求,从而提高访问效率。
  • 位操作优化:

    • 很多处理器都针对 2 的幂次方大小的数据提供了优化的指令集。
    • 比如,判断一个数是否是 2 的幂次方可以用位操作 (num & (num - 1)) == 0 来实现,这比普通的除法要高效得多。
  • 缓存友好性:

    • 当队列大小是 2 的幂次方时,元素在内存中的分布更加规则有序。
    • 这有利于利用缓存,减少缓存未命中的情况,提高整体性能。

3.2 索引计算

通过IndexOfQueue 函数,索引计算可以使用位与操作(&)而不是取模运算(%),从而提升计算速度。

3.3 原子操作与CAS

通过 std::atomic_compare_exchange_weak 原子操作实现无锁队列的核心逻辑。CAS(Compare-And-Swap)是无锁数据结构的基础,用于确保多个线程在修改共享数据时不会引发数据竞争。

3.4 线程让步

Enqueue 操作中,当多个线程尝试修改相同的共享变量时,失败的线程可以选择让出CPU时间片,以减少竞争和等待时间。

4. 测试示例程序

下面是一个简单的C++示例程序,演示了如何使用 LockFreeRingQueue 类。该程序将进行一些基本的入队和出队操作,然后在多线程环境下测试队列的使用。

4.1 实现代码

#include <iostream>
#include <thread>#include "lock_free_ring_queue.h"  // 假设你的类定义在这个文件中void SingleProducerSingleConsumerExample() {LockFreeRingQueue<int> queue(4);// 单生产者线程入队std::thread producer([&queue]() {for (int i = 0; i < 4; ++i) {if (queue.Enqueue(i)) {std::cout << "Producer enqueued: " << i << std::endl;} else {std::cout << "Queue is full, cannot enqueue: " << i << std::endl;}}});// 单消费者线程出队std::thread consumer([&queue]() {int value = 0;for (int i = 0; i < 4; ++i) {while (!queue.Dequeue(&value)) {std::this_thread::yield();  // 等待队列中有数据}std::cout << "Consumer dequeued: " << value << std::endl;}});producer.join();consumer.join();
}void MultiProducerMultiConsumerExample() {LockFreeRingQueue<int> queue(8);auto producer = [&queue](int id) {for (int i = 0; i < 4; ++i) {int value = id * 10 + i;if (queue.Enqueue(value)) {std::cout << "Producer " << id << " enqueued: " << value << std::endl;} else {std::cout << "Queue is full, Producer " << id << " cannot enqueue: " << value << std::endl;}}};auto consumer = [&queue](int id) {int value = 0;for (int i = 0; i < 4; ++i) {while (!queue.Dequeue(&value)) {std::this_thread::yield();  // 等待队列中有数据}std::cout << "Consumer " << id << " dequeued: " << value << std::endl;}};std::thread producers[2];std::thread consumers[2];// 启动两个生产者线程for (int i = 0; i < 2; ++i) {producers[i] = std::thread(producer, i);}// 启动两个消费者线程for (int i = 0; i < 2; ++i) {consumers[i] = std::thread(consumer, i);}for (auto &producer : producers) {producer.join();}for (auto &consumer : consumers) {consumer.join();}
}int main() {std::cout << "Single Producer, Single Consumer Example:" << std::endl;SingleProducerSingleConsumerExample();std::cout << "\nMulti Producer, Multi Consumer Example:" << std::endl;MultiProducerMultiConsumerExample();return 0;
}

4.2 代码解释:

  • SingleProducerSingleConsumerExample:

    • 创建了一个大小为 4 的队列。

    • 使用一个生产者线程将数字 0-3 入队。

    • 使用一个消费者线程从队列中出队并打印结果。

  • MultiProducerMultiConsumerExample:

    • 创建了一个大小为 8 的队列。

    • 启动两个生产者线程,每个线程将其线程 ID 和循环计数拼接成值并入队。

    • 启动两个消费者线程,从队列中出队并打印结果。

4.3 运行程序:

将上面的代码保存为 main.cpp,并确保你已经编译和链接了 LockFreeRingQueue 类的实现。

编译示例:

g++ -std=c++14 main.cpp -pthread -o lock_free_ring_queue_example
./lock_free_ring_queue_example

4.4 执行结果:

$ ./lock_free_ring_queue_example 
Single Producer, Single Consumer Example:
Producer enqueued: Consumer dequeued: 0
0
Producer enqueued: 1
Producer enqueued: 2
Producer enqueued: 3
Consumer dequeued: 1
Consumer dequeued: 2
Consumer dequeued: 3Multi Producer, Multi Consumer Example:
Producer 0 enqueued: 0
Producer 0 enqueued: 1
Producer 0 enqueued: 2
Producer 0 enqueued: 3
Consumer 1 dequeued: 0
Consumer 1 dequeued: 1
Consumer 1 dequeued: 2
Consumer 1 dequeued: 3
Producer 1 enqueued: 10
Producer 1 enqueued: 11
Producer 1 enqueued: 12
Producer 1 enqueued: 13
Consumer 0 dequeued: 10
Consumer 0 dequeued: 11
Consumer 0 dequeued: 12
Consumer 0 dequeued: 13

5. 单元测试

下面是使用gtest编写的LockFreeRingQueue类的完整单元测试。测试涵盖了基本功能,包括队列的初始化、入队、出队、边界条件,以及在多线程环境下的行为。

#include "lock_free_ring_queue.h"  // 假设你的类定义在这个文件中
#include <gtest/gtest.h>#include <algorithm>
#include <memory>
#include <thread>
#include <vector>class LockFreeRingQueueTest : public ::testing::Test {protected:void SetUp() override {// 初始化队列大小queue_size_ = 64;queue_ = std::make_unique<LockFreeRingQueue<int>>(queue_size_);}std::unique_ptr<LockFreeRingQueue<int>> queue_;uint32_t queue_size_;
};// 测试队列初始化
TEST_F(LockFreeRingQueueTest, Initialization) {EXPECT_EQ(queue_->Size(), 0U);EXPECT_TRUE(queue_->IsEmpty());
}// 测试入队和出队单个元素
TEST_F(LockFreeRingQueueTest, SingleEnqueueDequeue) {int value_in = 42;int value_out = 0;EXPECT_TRUE(queue_->Enqueue(value_in));EXPECT_EQ(queue_->Size(), 1U);EXPECT_FALSE(queue_->IsEmpty());EXPECT_TRUE(queue_->Dequeue(&value_out));EXPECT_EQ(value_out, value_in);EXPECT_EQ(queue_->Size(), 0U);EXPECT_TRUE(queue_->IsEmpty());
}// 测试队列满时入队
TEST_F(LockFreeRingQueueTest, EnqueueFullQueue) {for (uint32_t i = 0; i < queue_size_ - 1; ++i) {  // 注意减1EXPECT_TRUE(queue_->Enqueue(static_cast<int>(i)));}EXPECT_EQ(queue_->Size(), queue_size_ - 1);EXPECT_FALSE(queue_->Enqueue(100));  // 队列已满,入队失败
}// 测试空队列出队
TEST_F(LockFreeRingQueueTest, DequeueEmptyQueue) {int value_out = 0;EXPECT_FALSE(queue_->Dequeue(&value_out));  // 队列为空,出队失败
}// 多线程测试
TEST_F(LockFreeRingQueueTest, MultiThreadedEnqueueDequeue) {const int num_threads = 4;const int num_elements_per_thread = 10;auto enqueue_function = [&](int thread_id) {for (int i = 0; i < num_elements_per_thread; ++i) {queue_->Enqueue(thread_id * num_elements_per_thread + i);}};auto dequeue_function = [&](int thread_id, int* result_array) {for (int i = 0; i < num_elements_per_thread; ++i) {int value = 0;while (!queue_->Dequeue(&value)) {std::this_thread::yield();}result_array[thread_id * num_elements_per_thread + i] = value;}};std::vector<std::thread> threads;int results[num_threads * num_elements_per_thread] = {0};for (int i = 0; i < num_threads; ++i) {threads.emplace_back(enqueue_function, i);}for (auto& thread : threads) {thread.join();}threads.clear();for (int i = 0; i < num_threads; ++i) {threads.emplace_back(dequeue_function, i, results);}for (auto& thread : threads) {thread.join();}EXPECT_EQ(queue_->Size(), 0U);EXPECT_TRUE(queue_->IsEmpty());// 检查所有元素是否都已被成功出队std::sort(std::begin(results), std::end(results));for (int i = 0; i < num_threads * num_elements_per_thread; ++i) {EXPECT_EQ(results[i], i);}
}// 边界条件测试:初始化大小为1的队列
TEST(LockFreeRingQueueBoundaryTest, InitializationWithSizeOne) {LockFreeRingQueue<int> small_queue(1);EXPECT_EQ(small_queue.Size(), 0U);EXPECT_TRUE(small_queue.IsEmpty());int value_in = 99;EXPECT_TRUE(small_queue.Enqueue(value_in));EXPECT_FALSE(small_queue.Enqueue(value_in));  // 队列应该已经满了
}// 边界条件测试:入队和出队仅一个元素
TEST(LockFreeRingQueueBoundaryTest, SingleElementQueue) {LockFreeRingQueue<int> small_queue(1);int value_in = 123;int value_out = 0;EXPECT_TRUE(small_queue.Enqueue(value_in));EXPECT_FALSE(small_queue.Enqueue(value_in));  // 队列已满EXPECT_TRUE(small_queue.Dequeue(&value_out));EXPECT_EQ(value_out, value_in);EXPECT_FALSE(small_queue.Dequeue(&value_out));  // 队列为空
}int main(int argc, char** argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

5.1 测试代码解释:

  • 基本功能测试

    • Initialization: 检查队列是否正确初始化。

    • SingleEnqueueDequeue: 测试单个元素的入队和出队操作。

    • EnqueueFullQueue: 测试在队列已满时的入队操作。

    • DequeueEmptyQueue: 测试在队列为空时的出队操作。

  • 多线程测试

    • MultiThreadedEnqueueDequeue: 使用多个线程测试队列的入队和出队操作。每个线程分别执行入队和出队操作,最后检查所有元素是否正确出队。
  • 边界条件测试

    • InitializationWithSizeOne: 测试初始化大小为1的队列。

    • SingleElementQueue: 测试大小为1的队列的入队和出队操作。

5.2 测试运行:

将上面的代码保存为一个测试文件(例如lock_free_ring_queue_test.cpp),并确保你已经安装了gtest库。然后编译并运行测试。

编译示例:

g++ -std=c++14 lock_free_ring_queue_test.cpp -lgtest -lgtest_main -pthread -o lock_free_ring_queue_test
./lock_free_ring_queue_test

测试结果

$ ./lock_free_ring_queue_test 
[==========] Running 7 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 5 tests from LockFreeRingQueueTest
[ RUN      ] LockFreeRingQueueTest.Initialization
[       OK ] LockFreeRingQueueTest.Initialization (0 ms)
[ RUN      ] LockFreeRingQueueTest.SingleEnqueueDequeue
[       OK ] LockFreeRingQueueTest.SingleEnqueueDequeue (0 ms)
[ RUN      ] LockFreeRingQueueTest.EnqueueFullQueue
[       OK ] LockFreeRingQueueTest.EnqueueFullQueue(0 ms)
[ RUN      ] LockFreeRingQueueTest.DequeueEmptyQueue
[       OK ] LockFreeRingQueueTest.DequeueEmptyQueue (0 ms)
[ RUN      ] LockFreeRingQueueTest.MultiThreadedEnqueueDequeue
[       OK ] LockFreeRingQueueTest.MultiThreadedEnqueueDequeue (10 ms)
[----------] 5 tests from LockFreeRingQueueTest (10 ms total)[----------] Global test environment tear-down
[==========] 7 tests from 2 test suites ran. (10 ms total)
[  PASSED  ] 7 tests.

6. 总结

无锁环形队列在高并发场景下具有显著的性能优势,其设计充分利用了现代硬件提供的原子操作和内存模型。然而,在实际应用中,开发者需要权衡无锁结构带来的复杂性和潜在的硬件依赖问题。

相关文章:

C++编程:无锁环形队列 (LockFreeRingQueue)的简单实现、测试和分析

文章目录 0. 概述1. 无锁环形队列概述1.1 无锁环形队列的特点1.2 无锁环形队列的优势与局限 2. LockFreeRingQueue 实现2.1 Enqueue 操作流程图2.2 Dequeue 操作流程图 3. 核心实现细节3.1 环形队列的大小调整3.2 索引计算3.3 原子操作与CAS3.4 线程让步 4. 测试示例程序4.1 实…...

植物生长时为什么会扭动?科学家解开令查尔斯·达尔文困惑的千古之谜

在一项新的研究中&#xff0c;来自美国和以色列的物理学家可能已经弄清了植物生长过程中的一种古怪行为–也是查尔斯-达尔文本人在其生命的最后几十年里所好奇的一个谜&#xff1a;对于许多人类来说&#xff0c;植物可能看起来静止不动&#xff0c;甚至有点无趣。但实际上&…...

SAP LE学习笔记02 - WM和库存管理(IM)之间的关系,保管Lot(Quant)

上一章学习了LE的基础知识。 1&#xff0c;LE的概述&#xff0c;LE里面包含下面3个大的模块 - LE-WM 仓库管理 / - LE-SHP 发货/ - LE-TRA 运输 2&#xff0c;仓库的结构 - 仓库番号 / -保管域Type(存储区域)/ - 保管区画(存储区)/ - 棚番&#xff08;Storage Bin 仓位&…...

Span<T> 是 C# 7.2 引入的重要类型

Span<T> 是 C# 7.2 引入的一个非常重要的类型&#xff0c;它提供了一种低开销、类型安全的方式来操作连续的内存区域。Span<T> 本质上是一个结构体&#xff0c;它封装了一个内存段的引用&#xff08;通过指针&#xff09;以及该内存段的长度。由于它直接操作内存&a…...

Python办公自动化:初识 `openpyxl`

1.1 什么是 openpyxl&#xff1f; openpyxl 是一个用于读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。它允许我们通过 Python 脚本自动化处理 Excel 文件&#xff0c;包括创建新的工作簿、修改现有的工作簿、格式化单元格、处理公式和图表等功能。这对于办公自动化、…...

Pocketbase实战体验:内置数据库与实时功能如何超越传统MySQL

Pocketbase 是一个开源的实时后端服务器&#xff0c;内置了数据库、实时订阅、用户认证、RESTful API 等功能&#xff0c;而 MySQL 是一个广泛使用的关系数据库管理系统。以下是 Pocketbase 相对于 MySQL 的一些潜在优点&#xff1a; 完整的后端解决方案 一体化&#xff1a;P…...

ChatGPT 3.5/4.0 新手使用手册(详细版)

1. 什么是 ChatGPT&#xff1f; ChatGPT是由 OpenAI 开发的先进人工智能语言模型&#xff0c;能够理解并生成自然语言文本。它可以帮助你进行写作、回答问题、提供建议&#xff0c;甚至参与对话。ChatGPT 3.5 和 4.0 是两个不同版本&#xff0c;它们都拥有强大的语言处理能力&…...

【Java学习】Stream流详解

所属专栏&#xff1a;Java学习 Stream流是JDK 8引入的一个概念&#xff0c;它提供了一种高效且表达力强的方式来处理数据集合&#xff08;如List、Set等&#xff09;或数组。Stream API可以以声明性方式&#xff08;指定做什么&#xff09;来处理数据序列。流操作可以被分为两大…...

Oracle(69)什么是表压缩(Table Compression)?

表压缩&#xff08;Table Compression&#xff09;是一种数据库优化技术&#xff0c;用于减少表数据的存储空间和提高I/O性能。通过压缩表数据&#xff0c;可以显著减少存储需求&#xff0c;并在某些情况下提高查询性能&#xff0c;特别是对于只读或主要是读取操作的表。表压缩…...

java JUC编程

Java并发工具包&#xff08;JUC&#xff09;&#xff0c;全称Java Util Concurrent&#xff0c;是Java提供的一个用于构建多线程应用程序的工具包&#xff0c;位于java.util.concurrent包及其子包中。 并发编程主要解决以下三个经典问题&#xff1a; 1. **原子性问题&#xf…...

vue3+element-plus表格分页选中加默认回显选中

1.需求 某个表单需要选择多条数据&#xff0c;点击选择按钮&#xff0c;弹框出来一个分页列表&#xff0c;选择多条数据&#xff0c;外面表单中显示选中的数据&#xff0c;可以删除数据&#xff0c;再次点击按钮&#xff0c;回显当前选中的数据。 2.解决办法 1.el-table加ro…...

Erupt 项目搭建

创建Spring Boot项目 Maven依赖 Spring Boot版本为 2.7.10&#xff0c;erupt版本为 1.12.14 erupt版本要与Spring Boot版本适配&#xff0c;3.x.x版本Spring Boot暂不适用说是 <properties><erupt.version>1.12.14</erupt.version></properties> <…...

HarmonyOS Next 系列之列表下拉刷新和触底加载更多数据实现(十一)

系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现&#xff08;一&#xff09; HarmonyOS Next 系列之验证码输入组件实现&#xff08;二&#xff09; HarmonyOS Next 系列之底部标签栏TabBar实现&#xff08;三&#xff09; HarmonyOS Next 系列之HTTP请求封装和Token…...

比特位的计算

给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;[0,1,1] 解释&#xff1a; 0 --> 0 1 --> …...

SQLAlchemy 学习笔记

通信类型&#xff1a;AF_INET 协议家族一般是表示TCP通信的SOC_STREAM和UDP通信的SOCK_DGRAM。对于TCP通信&#xff0c;建立socket连接&#xff0c;&#xff1a; s socket.socket(socket.AF_INET, socket.SOCK_STREAM)连接socket&#xff0c; s.connect((host,port))socket通信…...

Linux内核分析(调度类和调度实体)

文章目录 前言一、调度类1. stop_sched_class2. dl_sched_class3. rt_sched_class4. fair_sched_class5. idle_sched_class总结 二、调度类中的操作函数三、调度实体 前言 调度是操作系统内核的一个关键职责&#xff0c;它涉及到如何合理分配CPU时间给不同的进程或线程。在Lin…...

用输入输出流(I/O)流,递归复制和删除多级文件

一、&#xff08;I/O&#xff09;流递归复制一个文件 第一种&#xff1a; else if语句过多&#xff0c;看起来冗余&#xff0c;优点&#xff1a;多级文件一次性复制完整 import java.io.*;//数据源&#xff1a;src/main/java/day15_8_13/haha //目标;src/main/java/LaJi pub…...

kafka监控工具EFAK

kafka监控工具&#xff08;EFAK&#xff09; 1、下载2、解压3、配置3.1、安装数据库&#xff0c;需要mysql是&#xff0c;并创建ke数据库3.2、修改配置文件 4、启动4.1、启动zookeeper4.2、启动kafka4.3、启动EFAK 5、访问http://ip:8048 github地址&#xff1a;https://github…...

Page与自定义Components生命周期

自定义组件 自定义组件一般可以用component,装饰&#xff0c;在结构体里面用build方法定义UI,或者用builder装饰一个方法&#xff0c;来作为自定义组件的构造方法 而页面page一般用Entry,和component结合起来使用 页面生命周期方法: onPageShow:页面每次显示时触发 onPageHid…...

Chain of Thought (CoT) 系列论文:大模型思维链,提升 LLM 的推理能力

文章目录 1. COT&#xff1a;Chain of Thought1. 研究背景2. CoT的原理3. CoT Prompt 1. COT&#xff1a;Chain of Thought COT 是 2022.01 由 google 提出的针对提升 LLM 的推理能力的 Prompt Engineering 方法。 paper&#xff1a; Chain-of-Thought Prompting Elicits Re…...

已解决:java.net.BindException: 地址已在使用

1. 问题描述 java.net.BindException: 地址已在使用 是一种常见的网络异常&#xff0c;通常在服务器程序尝试绑定到一个已经被占用的端口或地址时出现。具体的异常信息可能如下&#xff1a; java.net.BindException: Address already in use: JVM_Bind或 java.net.BindExcep…...

看书标记【数据科学:R语言实战 8】

看书标记——R语言 Chapter 8 数据可视化——绘图8.1 功能包8.2 散点图8.2.1 回归线8.2.2 lowess线条8.2.3 scatterplot函数8.2.4 Scatterplot矩阵1.splom——展示矩阵数据2.cpairs——绘图矩阵图 8.2.5 密度散点图 8.3 直方图和条形图8.3.1 条形图8.3.2 直方图 8.3.3 ggplot28…...

STM32标准库学习笔记-1.基础知识

STM32介绍&#xff1a; STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器。 ARM的含义&#xff1a; 公司名称&#xff1a;ARM公司成立于1990年&#xff0c;全称是Advanced RISC Machines&#xff08;RISC:Reduced Instruction Set Computer 精简指令集计算机 相对应有C…...

Nginx:高效HTTP服务器与反向代理

Nginx&#xff1a;高效HTTP服务器与反向代理 1、核心特点2、应用场景 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Nginx&#xff0c;一个开源的HTTP服务器与反向代理工具&#xff0c;因其高性能、低资源消耗而备受推崇。以下是Nginx的几…...

vue3二次封装element-puls

将表单的通用信息给设置出来 如: label 的提示信息 , type 的类型 // 定义表单的配置项 const formConfig{ formItems:[ { type:"input", label:"用户ID", placeholder:"请输入用户ID" } ] } 页面配置如 <template v-for"(it…...

在CentOS 7上安装Apache Tomcat 8的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 介绍 Apache Tomcat 是一个用于提供 Java 应用程序的 Web 服务器和 Servlet 容器。Tomcat 是由 Apache 软件基金会发布的 Java Servlet…...

深入理解分布式事务中的三阶段提交(3PC),什么是3PC,3PC原理是怎样?3PC的优化?

在上一篇文章中&#xff0c;我们详细介绍了分布式事务中的两阶段提交&#xff0c;以及知道了两阶段提交存在一定的问题 深入理解分布式事务中的两阶段提交&#xff08;2PC&#xff09;&#xff0c;什么是2PC&#xff0c;2PC原理是怎样&#xff1f;2PC有没有什么问题&#xff1…...

这款新的 AI 工具会消灭 ChatGPT 吗?

随着大型语言模型 (LLM) 的出现&#xff0c;ChatGPT迅速成为全球计算机用户的家喻户晓的名字。这款由 OpenAI 设计的深度学习聊天机器人以知识宝库而闻名——一部互联网百科全书。 继ChatGPT的脚步之后&#xff0c;许多其他生成式AI工具也纷纷涌现。 2023 年 3 月&#xff0c;一…...

谷粒商城实战笔记-214~219-商城业务-认证服务-验证码防刷校验

文章目录 一&#xff0c;验证码防刷校验1&#xff0c;第三方服务提供发送短信的接口2&#xff0c;登录服务提供给前端的接口 二&#xff0c;215-商城业务-认证服务-一步一坑的注册页环境三&#xff0c;商城业务-认证服务-异常机制四&#xff0c;217-商城业务-认证服务-MD5&…...

在华为服务器的openEuler系统中适配Pytorch调用NPU

服务器架构&#xff1a;aarch64 yolov7 和 mindyolo 二选一即可&#xff0c;yolov7是基于pytorch&#xff0c;mindyolo是基于mindspore 本文档基于CANN8.0RC3 , 刚发布比较新&#xff0c;如果有问题&#xff0c;可将CANN版本降低 导读 资料首页&#xff1a;https://www.hiasce…...

MVCC工作原理深入解析

一、事务概述 mysql事务是指一组命令操作&#xff0c;在执行过程中用来保证要么全部成功&#xff0c;要么全部失败。事务是由引擎层面来支持的&#xff0c;MyISM引擎不支持事务&#xff0c;InnoDB引擎支持事务。 事务具有ACID四大特性 原子性&#xff08;Atomicity&#xff0…...

使用html+css+js实现完整的登录注册页面

在这篇博客中&#xff0c;我们将讨论如何使用简单的 HTML 和 CSS 构建一个登录与注册页面。这个页面包含两个主要部分&#xff1a;登录界面和注册界面。我们还会展示如何通过 JavaScript 切换这两个部分的显示状态。 页面结构 我们将创建一个页面&#xff0c;其中包含两个主要…...

2024年8月16日(运维自动化 ansible)

一、回顾 1、mysql和python (1)mysql5.7 1.1不需要执行mysql_ssl_rsa_setup 1.2change_master_to 不需要get public key (2)可以使用pymysql非交互的管理mysql 2.1pymysql.connect(host,user,password,database,port) 2.2 cursorconn.cursor() 2.3 cursor.execute("creat…...

荣耀Magicbook x14 扩容1TB固态

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 固态硬盘规格 在官网查看加装固态硬盘的接口规格 https://www.honor.com/cn/laptops/honor-magicbook-x14-2023/ https://club.honor.com/cn/thread-2847379…...

Springboot整合全文检索引擎Lucene

文章目录 前言Lucene的介绍springboot项目中如何整合Lucene简单用法1. 引入依赖2. 其它用到的类2. 创建索引3. 简单搜索4. 更新索引5. 删除索引6. 删除全部索引 Springboot整合Lucene复杂搜索1. 同时标题和内容中查找关键词2. 搜索结果高亮显示关键词3. 分页搜索4. 多关键词联合…...

【深度学习】【语音】TTS, 如何使用Python分析WAV的采样率、比特深度、通道数

文章目录 使用Python分析WAV文件的属性与可视化简介所需环境代码解析可视化音频数据结论使用Python分析WAV文件的属性与可视化 WAV文件录音要求 为了确保录制的音频文件符合TTS模型训练的质量标准,请遵循以下录音要求: 采样率要求:44.1 kHz说明:采样率44.1 kHz(即每秒采样…...

Linux的安装和使用

Linux 第一节 Linux 优势 1. 开源 为什么这么多的的设备都选择使用 Linux&#xff1f;因为它是开源软件&#xff08;open source software&#xff09;&#xff0c;具有不同的含义。使用一个安全的操作系统工作变得必不可少的事&#xff0c;而 Linux 恰好满足了这个需求。因…...

查看一个exe\dll文件的依赖项

方法 使用一个Dependencies工具&#xff0c;检测exe文件的所有依赖项 工具使用 下载压缩包之后解压&#xff0c;解压后如下图所示 在命令行中运行Dependencies.exe程序会得到帮助菜单 查询某exe的所有依赖项&#xff0c;使用命令 Dependencies.exe -chain <查询文件> …...

高校科研信息管理系统pf

TOC springboot364高校科研信息管理系统pf 第1章 绪论 1.1 研究背景 互联网概念的产生到如今的蓬勃发展&#xff0c;用了短短的几十年时间就风靡全球&#xff0c;使得全球各个行业都进行了互联网的改造升级&#xff0c;标志着互联网浪潮的来临。在这个新的时代&#xff0c;…...

Linux 开机自动挂载共享文件设置

选择一个要共享的文件 点击确定 -> 确定 启动虚拟机 执行下面的命令 /YumSource 是我选择的共享文件夹&#xff0c;自行替换自已选择的文件夹 mkdir -p /mnt/hgfs cat >> /etc/fstab << EOF .host:/YumSource /mnt/hgfs fuse.vmhgfs-fuse allow_other defaul…...

c_cpp_properties.json、launch.json、 tasks.json

在 Visual Studio Code 中&#xff0c;c_cpp_properties.json、launch.json 和 tasks.json 是三个重要的配置文件&#xff0c;它们的作用如下&#xff1a; c_cpp_properties.json&#xff1a; 这个文件用于配置 C/C 扩展的 IntelliSense、编译器路径和包括路径等。它帮助 VS Co…...

mysql 一些知识点 面试用

mysql 1、4个隔离级别与3个现象2、快照读与当前读2.1 可重复读的情况下出现幻读问题的两种情况 3 数据库 常用引擎4、InnoDB存储引擎对MVCC的实现5、索引(重点)5.1 什么是索引5.2 索引的创建与删除5.2.1 查看表中有哪些索引5.2.2 添加索引5.2.3 删除索引 5.3 索引的分类5.4 树数…...

STM32之点亮LED灯

使用固件库实现LED点灯 LED灯&#xff1a; LED灯&#xff0c;是一种能够将电能转化为可见光的半导体器件 控制LED灯&#xff1a; LED灯的正极接到了3.3V&#xff0c;LED灯的负极接到了PA1&#xff0c;也就是GPIOA1引脚 只需要控制PA1为相对应的低电平&#xff0c;即可点亮对…...

Java 多线程练习2 (抽奖比较Runnable写法)

MultiProcessingExercise2 package MultiProcessingExercise120240814;import java.util.ArrayList; import java.util.Collections;public class MultiProcessingExercise1 {public static void main(String[] args) {// 需求&#xff1a;// 在此次抽奖过程中&#xff0c;抽奖…...

使用fastboot更新部分系统

使用fastboot更新部分系统 获取分区信息 > part list sunxi_flash 0Partition Map for UNKNOWN device 0 -- Partition Type: EFIPart Start LBA End LBA NameAttributesType GUIDPartition GUID1 0x00008000 0x000097c5 "boot-r…...

windows 加载portch遇到的错误

import torch 遇到如下错误 File "<stdin>", line 1, in <module> File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\torch\__init__.py", line 148, in <module> raise err OSError: [W…...

如何将 CICD 模版重构为 CICD component?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…...

数学建模——评价决策类算法(层次分析法、Topsis)

一、层次分析法 概念原理 通过相互比较确定各准则对于目标的权重, 及各方案对于每一准则的权重&#xff0c;这些权重在人的思维过程中通常是定性的, 而在层次分析法中则要给出得到权重的定量方法. 将方案层对准则层的权重及准则层对目标层的权重进行综合, 最终确定方案层对目标…...

KEEPALIVED 全csdn最详细----理论+实验(干货扎实,包教会的)

环境准备 主机名IP虚拟IP&#xff08;VIP&#xff09;功能ka1172.25.254.10172.25.254.100keepalived服务ka2172.25.254.20172.25.254.100keepalived服务realserver1172.25.254.110web服务realserver2172.25.254.120web服务 注意一定要关闭selinux,和防火墙&#xff0c;不然在…...

微信云开发云存储全部下载

一、安装 首先按照这个按照好依赖 安装 | 云开发 CloudBase - 一站式后端云服务 npm i -g cloudbase/cli 二、登录 tcb login 下载 首先在你要下载到的本地文件内创建一个名为&#xff1a;cloudbaserc.json 的json文件。 填入你的id {"envId":"你的云开发环…...