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

学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代

引言

        随着多核处理器的普及,多线程编程技术已经成为提高应用程序性能的关键手段。在多线程环境下,如何高效、安全地管理线程之间的共享资源是开发者面临的主要挑战。传统的锁机制,如互斥锁(Mutex)、临界区(Critical Section)等,虽然能有效防止数据竞争,但也存在性能瓶颈。在高并发场景下,这些锁可能会导致线程频繁阻塞,从而降低整体性能。因此,锁优化和无锁编程技术应运而生,旨在提高多线程程序的性能和扩展性。

        本文将深入探讨几种高级多线程编程技术,包括自动锁、读写锁、无锁编程技术。我们将结合经典示例和详细解析,帮助开发者掌握这些技术的核心概念和使用技巧。

1. 自动锁(RAII锁)

1.1 自动锁的概念

        自动锁(RAII锁)是一种利用C++的RAII(Resource Acquisition Is Initialization)机制自动管理锁的技术。RAII锁通过构造函数获取锁,通过析构函数释放锁,从而避免了手动管理锁的繁琐,并且在异常处理或函数提前返回的情况下,确保锁能够正确释放。

1.2 示例:使用 std::lock_guard 实现自动锁

std::lock_guard 是 C++11 提供的自动锁工具,它能够确保在作用域结束时自动释放锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int counter = 0;void incrementCounter() {std::lock_guard<std::mutex> lock(mtx);  // 自动获取和释放锁++counter;std::cout << "Counter: " << counter << std::endl;
}int main() {std::thread t1(incrementCounter);std::thread t2(incrementCounter);t1.join();t2.join();std::cout << "Final counter: " << counter << std::endl;return 0;
}

1.3 运行结果

Counter: 1
Counter: 2
Final counter: 2

这段C++程序展示了如何使用std::mutexstd::lock_guard来确保线程安全地更新一个共享变量counter。下面是对程序各部分的解释:

  1. 包含头文件:

    • #include <iostream>: 包含输入输出流库,用于标准输入输出操作。
    • #include <thread>: 包含线程库,用于创建和管理线程。
    • #include <mutex>: 包含互斥量库,用于实现同步机制。
  2. 全局变量声明:

    • std::mutex mtx;: 声明一个互斥量对象mtx,用于保护对counter的访问。
    • int counter = 0;: 声明一个整型变量counter,初始值为0,该变量将在多个线程间共享。
  3. 函数定义:

    • void incrementCounter(): 定义了一个名为incrementCounter的函数,用于增加counter的值。
      • std::lock_guard<std::mutex> lock(mtx);: 使用std::lock_guard自动锁定互斥量mtx,确保函数体内的代码原子性执行。当lock_guard对象生命周期结束时,互斥量自动解锁。
      • ++counter;: 增加counter的值。
      • std::cout << "Counter: " << counter << std::endl;: 输出当前counter的值。
  4. 主函数:

    • std::thread t1(incrementCounter);: 创建一个线程t1,它将调用incrementCounter函数。
    • std::thread t2(incrementCounter);: 创建另一个线程t2,同样调用incrementCounter函数。
    • t1.join();: 等待t1线程执行完成。
    • t2.join();: 等待t2线程执行完成。
    • std::cout << "Final counter: " << counter << std::endl;: 输出最终的counter值。
  5. 程序行为:

    • t1t2两个线程并发运行时,由于incrementCounter函数使用了std::lock_guard来锁定mtx,因此每次只有一个线程能够进入函数体并增加counter的值。
    • 这样可以避免竞态条件,保证counter的值正确更新。
    • std::lock_guardstd::lock_guard<std::mutex> lock(mtx);: 这行代码创建了一个std::lock_guard对象lock,并传入互斥量mtx作为构造函数的参数。std::lock_guard是一个RAII(Resource Acquisition Is Initialization)风格的类模板,它会在构造时自动锁定给定的互斥量,并在析构时自动解锁。
    • 锁的自动释放:

         当incrementCounter函数执行完毕,std::lock_guard对象lock的生命周期结束,mtx互斥量会自动解锁。

      总结来说,这段程序演示了如何使用std::mutexstd::lock_guard来实现线程间的同步,确保共享资源的安全访问。

1.4 核心点解析

自动锁的关键在于它将锁的获取和释放操作封装在对象的生命周期内,利用C++的作用域规则自动管理锁的生存期。使用 std::lock_guard 可以有效避免因忘记释放锁而导致的死锁问题,提高代码的安全性和可维护性。

2. 读写锁(Shared Mutex)

2.1 读写锁的概念

读写锁(Shared Mutex)是一种允许多个线程同时读取共享资源,但在写入时只允许一个线程操作的锁机制。这种锁能够显著提高多线程程序的并发性能,特别是在读操作远多于写操作的情况下。

2.2 示例:使用 std::shared_mutex 实现读写锁

C++17 引入了 std::shared_mutex,它允许多个线程同时持有读锁(共享锁),但在写锁(独占锁)被持有时,其他线程的任何读写操作都会被阻塞。

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>std::shared_mutex rw_mtx;
std::vector<int> data;void readData(int thread_id) {std::shared_lock<std::shared_mutex> lock(rw_mtx);  // 读锁std::cout << "Thread " << thread_id << " reading data: ";for (const auto& item : data) {std::cout << item << " ";}std::cout << std::endl;
}void writeData(int value) {std::unique_lock<std::shared_mutex> lock(rw_mtx);  // 写锁data.push_back(value);std::cout << "Writing data: " << value << std::endl;
}int main() {std::thread writer1(writeData, 1);std::thread writer2(writeData, 2);std::thread reader1(readData, 1);std::thread reader2(readData, 2);writer1.join();writer2.join();reader1.join();reader2.join();return 0;
}

2.3 运行结果

Writing data: 1
Writing data: 2
Thread 1 reading data: 1 2 
Thread 2 reading data: 1 2 

这段C++程序展示了如何使用std::shared_mutex(读写锁)来实现多线程环境下对共享数据data的读写操作。下面是程序各部分的详细解释:

  1. 包含头文件:

    • #include <iostream>: 包含输入输出流库,用于标准输入输出操作。
    • #include <thread>: 包含线程库,用于创建和管理线程。
    • #include <shared_mutex>: 包含读写锁库,用于实现读写锁机制。
    • #include <vector>: 包含向量容器库,用于存储共享数据。
  2. 全局变量声明:

    • std::shared_mutex rw_mtx;: 声明一个读写锁对象rw_mtx,用于保护对data的访问。
    • std::vector<int> data;: 声明一个整型向量data,用于存储共享数据。
  3. 函数定义:

    • void readData(int thread_id): 定义了一个名为readData的函数,用于读取data向量中的数据。
      • std::shared_lock<std::shared_mutex> lock(rw_mtx);: 使用std::shared_lock自动获取读锁,允许多个线程同时读取数据。
      • for (const auto& item : data): 遍历data向量并打印其中的元素。
    • void writeData(int value): 定义了一个名为writeData的函数,用于向data向量中添加新元素。
      • std::unique_lock<std::shared_mutex> lock(rw_mtx);: 使用std::unique_lock自动获取写锁,确保一次只有一个线程能够写入数据。
      • data.push_back(value);: 向data向量中添加新元素。
  4. 主函数:

    • std::thread writer1(writeData, 1);: 创建一个线程writer1,它将调用writeData函数并将值1传递给它。
    • std::thread writer2(writeData, 2);: 创建另一个线程writer2,同样调用writeData函数并将值2传递给它。
    • std::thread reader1(readData, 1);: 创建一个线程reader1,它将调用readData函数,并传递线程标识符1。
    • std::thread reader2(readData, 2);: 创建另一个线程reader2,同样调用readData函数,并传递线程标识符2。
    • writer1.join();: 等待writer1线程执行完成。
    • writer2.join();: 等待writer2线程执行完成。
    • reader1.join();: 等待reader1线程执行完成。
    • reader2.join();: 等待reader2线程执行完成。
  5. 程序行为:

    • std::shared_mutex是一种读写锁,允许多个线程同时读取数据(即可以有多个读锁),但一次只能有一个线程写入数据(即只能有一个写锁)。
    • std::shared_lock用于读操作,它允许同时存在多个std::shared_lock实例,即多个线程可以同时读取数据。
    • std::unique_lock用于写操作,确保在写入数据时不会有其他线程读取或写入数据。
    • writer1writer2尝试写入数据时,它们必须等待,直到没有任何其他线程持有写锁或读锁。
    • reader1reader2尝试读取数据时,它们可以同时读取数据,因为读锁之间不相互排斥。

总结来说,这段程序演示了如何使用std::shared_mutex来实现线程间的同步,确保读写操作的正确性。std::shared_mutex的使用可以提高程序的并发性能,尤其是在读操作远多于写操作的情况下。

2.4 核心点解析

读写锁的主要优势在于它提高了读操作的并发性,适合读多写少的场景。使用 std::shared_mutex 可以让多个读线程并行执行,从而提升程序的性能。然而,需要注意的是,在写锁持有时,所有读写操作都会被阻塞,因此在写操作频繁的情况下,读写锁可能无法带来显著的性能提升。

3. 无锁编程技术

3.1 无锁编程的概念

无锁编程技术旨在通过避免使用传统的锁机制来提高多线程程序的性能和扩展性。在无锁编程中,通常使用原子操作和内存模型来确保线程安全,并避免了锁带来的上下文切换和竞争开销。

3.2 原子操作:std::atomic

C++11 引入了 std::atomic 模板类,它提供了一组简单、高效的原子操作接口,能够在多线程环境下确保变量的原子性操作。

示例:使用 std::atomic 实现无锁计数器
#include <iostream>
#include <thread>
#include <atomic>std::atomic<int> atomic_counter(0);void incrementAtomicCounter() {for (int i = 0; i < 10000; ++i) {atomic_counter.fetch_add(1, std::memory_order_relaxed);}
}int main() {std::thread t1(incrementAtomicCounter);std::thread t2(incrementAtomicCounter);t1.join();t2.join();std::cout << "Final atomic counter: " << atomic_counter << std::endl;return 0;
}

 3.3 运行结果

Final atomic counter: 20000

3.4 无锁队列

无锁队列是一种先进先出(FIFO)的数据结构,使用无锁技术来确保线程安全。Michael & Scott (M&S) 队列是最著名的无锁队列之一,通过使用原子操作实现队列的无锁入队和出队操作。

示例:使用 std::atomic 实现简单的无锁队列
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>template<typename T>
class LockFreeQueue {
private:struct Node {T data;Node* next;Node(T value) : data(value), next(nullptr) {}};std::atomic<Node*> head;std::atomic<Node*> tail;public:LockFreeQueue() {Node* dummy = new Node(T());head.store(dummy);tail.store(dummy);}~LockFreeQueue() {while (Node* node = head.load()) {head.store(node->next);delete node;}}void enqueue(T value) {Node* new_node = new Node(value);Node* old_tail = nullptr;while (true) {old_tail = tail.load();Node* next = old_tail->next;if (old_tail == tail.load()) {if (next == nullptr) {if (std::atomic_compare_exchange_weak(&(old_tail->next), &next, new_node)) {break;}} else {std::atomic_compare_exchange_weak(&tail, &old_tail, next);}}}std::atomic_compare_exchange_weak(&tail, &old_tail, new_node);}bool dequeue(T& result) {Node* old_head = nullptr;while (true) {old_head = head.load();Node* old_tail = tail.load();Node* next = old_head->next;if (old_head == head.load()) {if (old_head == old_tail) {if (next == nullptr) {return false;  // 队列空}std::atomic_compare_exchange_weak(&tail, &old_tail, next);} else {result = next->data;if (std::atomic_compare_exchange_weak(&head, &old_head, next)) {break;}}}}delete old_head;return true;}
};void producer(LockFreeQueue<int>& queue) {for (int i = 0; i < 100; ++i) {queue.enqueue(i);}
}void consumer(LockFreeQueue<int>& queue) {int value;for (int i = 0; i < 100; ++i) {while (!queue.dequeue(value)) {// Busy-wait until a value is dequeued}std::cout << "Consumed: " << value << std::endl;}}
}int main() {LockFreeQueue<int> queue;std::thread prod1(producer, std::ref(queue));std::thread prod2(producer, std::ref(queue));std::thread cons1(consumer, std::ref(queue));std::thread cons2(consumer, std::ref(queue));prod1.join();prod2.join();cons1.join();cons2.join();return 0;
}

3.5 运行结果

由于输出顺序可能有所不同,以下是典型的输出结果示例:

Consumed: 0
Consumed: 1
Consumed: 2
...
Consumed: 99
Consumed: 0
Consumed: 1
...
Consumed: 99

3.6 核心点解析

        无锁编程技术通过使用原子操作和正确的内存序来确保并发操作的安全性。std::atomic 提供了一种简单的方式来实现基本的无锁数据结构,而更复杂的数据结构(如无锁队列)则需要使用更复杂的原子操作和算法。

        无锁编程的主要优势在于它避免了锁的开销,减少了线程间的阻塞和上下文切换。然而,实现无锁算法通常比锁机制更复杂,需要开发者深入理解硬件级的并发控制机制,如原子操作和内存屏障。此外,无锁编程并不能完全消除竞争条件,还需要特别注意数据结构的一致性和正确性。

        无锁队列是一种典型的无锁数据结构,通过设计合理的算法和使用原子操作,可以有效地避免竞争条件和死锁问题。需要注意的是,无锁数据结构的设计往往比锁机制更复杂,并且更容易出错,因此在使用时需要仔细验证其正确性和性能。

4. 结合场景进行技术选择

        在实际的多线程编程中,选择适当的并发控制技术至关重要。以下是几种常见的场景以及相应的技术选择建议:

  1. 简单的临界区保护:
    如果只是保护简单的临界区,使用 std::lock_guard 是最为直接和安全的选择。它能够简化代码,并减少因忘记解锁而引发的死锁风险。

  2. 读多写少的场景:
    如果读操作远多于写操作,使用 std::shared_mutex 这种读写锁可以显著提高性能。多个读线程可以并发执行,而写线程仍然能够确保数据的一致性。

  3. 高性能需求且争用严重:
    在需要极高性能、且线程间争用严重的场景中,无锁编程技术可能是最佳选择。虽然无锁编程复杂且容易出错,但它能够避免锁的开销,减少线程阻塞,提升整体性能。

  4. 生产者-消费者模式:
    这种模式非常适合使用无锁队列进行数据传递。生产者和消费者可以独立并行地工作,避免了锁带来的性能损耗。

  5. 需要细粒度锁控制:
    在某些复杂的场景下,可能需要更细粒度的锁控制以优化性能。这时,可以结合使用条件变量、独占锁和共享锁,甚至是自定义的锁机制来进行精细化控制。

5. 总结

本文详细介绍了C++高级多线程编程中的几种重要技术:自动锁、读写锁和无锁编程。通过具体的代码示例,展示了如何使用这些技术来优化多线程程序的性能。

  • 自动锁 提供了简洁、安全的锁管理方式,减少了手动管理锁带来的错误风险。
  • 读写锁 通过区分读操作和写操作,允许多个线程并发读取数据,特别适合读多写少的场景。
  • 无锁编程 通过使用原子操作避免了传统锁带来的性能开销,是高性能并发编程的关键技术,但其实现复杂度较高。

        在实际开发中,选择合适的锁机制或无锁技术应根据具体的应用场景、性能需求以及程序的复杂性进行权衡。掌握这些技术不仅能提高多线程程序的性能,还能为开发高效、健壮的并发应用程序奠定坚实的基础。

        希望通过本文的详细解析,能够帮助开发者深入理解C++多线程编程中的锁优化与替代技术,提升在多线程开发中的实践能力。

上一篇:学懂C++(三十):高级教程——深入解析 C++ Windows API 的多线程支持 

下一篇:学懂C++(三十二):深入详解C++高级多线程编程技术:内存模型与顺序一致性

 

相关文章:

学懂C++(三十一):高级教程——深入详解C++高级多线程编程技术之锁优化与替代

引言 随着多核处理器的普及&#xff0c;多线程编程技术已经成为提高应用程序性能的关键手段。在多线程环境下&#xff0c;如何高效、安全地管理线程之间的共享资源是开发者面临的主要挑战。传统的锁机制&#xff0c;如互斥锁&#xff08;Mutex&#xff09;、临界区&#xff08;…...

Linux - 基础工具使用

文章目录 一、yum1、介绍2、功能3、语法4、使用 二、rzsz1、安装rzsz的指令2、介绍3、使用 三、vim基础使用1、介绍2、基础使用 四、gcc/g使用1、生成可执行文件过程2、语法3、常用选项4、编译过程5、动静态库6、包含头文件的多文件编译7、链接外部库 一、yum 1、介绍 Linux中…...

理解线程id和简单封装原生线程库

一、理解线程id 首先我们要知道给用户提供的线程id不是内核里面LWP&#xff08;轻量级进程id&#xff09;&#xff0c;而是pthread库自己维护的一个唯一值。 我们理解为什么线程id不是内核里面LWP&#xff0c;因为用户没有权限使用内核里面的字段&#xff0c;那是专门给OS管理…...

Unified 阻抗控制 architecture、framework、approach

Unified 阻抗控制&#xff08;Unified Impedance Control&#xff09;作为一种控制策略&#xff0c;其architecture&#xff08;架构&#xff09;、framework&#xff08;框架&#xff09;和approach&#xff08;方法&#xff09;为&#xff1a; 一、Unified 阻抗控制 Archite…...

Java后端面试题(mq相关)(day9)

目录 为什么用MQ&#xff1f; 异步 、削峰、解耦1. 异步处理2. 解耦3. 削峰填谷 Exchange类型什么是死信队列&#xff1f;如何保证消息的可靠性&#xff1f;RabbitMQ中如何解决消息堆积问题?RabbitMQ中如何保证消息有序性?如何防止消息重复消费&#xff1f;(如何保证消息幂等…...

算法-华为OD机试-识别有效的IP地址和掩码并进行分类统计

1.描述 见牛客网 https://www.nowcoder.com/practice/de538edd6f7e4bc3a5689723a74356822. 分析 根据题目要求&#xff0c;分为以下几步 1. 提取IP地址和子网掩码 我们首先需要拆分输入的每一行&#xff0c;分别提取IP地址和子网掩码&#xff0c;并检查它们的合法性。 2.…...

钉钉开发网页应用JSAPI前端授权鉴权nodejs实现

钉钉开发网页应用JSAPI前端授权鉴权nodejs实现 使用钉钉进行H5网页开发的时候&#xff0c;需要调用一些钉钉提供具有原生能力的api&#xff0c;要调用这些api需要进行jsapi授权。 详见官方文档&#xff08;可选&#xff09;开发网页应用前端 - 钉钉开放平台 (dingtalk.com) 官方…...

uniapp 自定义全局弹窗

自定义全局弹窗可在js和.vue文件中调用&#xff0c;unipop样式不满足&#xff0c;需自定义样式。 效果图 目录结构 index.vue <template><view class"uni-popup" v-if"isShow"><view class"uni-popup__mask uni-center ani uni-cust…...

element+-ui图片无法使用--安装

element-ui图片无法使用 安装npm install element-plus/icons-vue 注册 // main.jsimport * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, compo…...

Python编码系列—Python ORM(对象关系映射):高效数据库编程实践

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…...

一次日志记录中使用fastjson涉及到ByteBuffer的教训

背景 目前本人在公司负责的模块中&#xff0c;有一个模块是负责数据同步的&#xff0c;主要是将我们数据产线使用的 AWS Dynamodb 同步的我们的测试QA 的环境的 MongoDB 的库中&#xff0c;去年开始也提供了使用 EMR 批量同步的功能&#xff0c;但是有时候业务也需要少量的数据…...

掌握TCP连接管理与流量控制:从零开始

文章目录 1. TCP连接管理1.1 三次握手&#xff08;Three-way Handshake&#xff09;1.2 四次挥手&#xff08;Four-way Handshake&#xff09;1.3 TCP连接管理的重要性 2. TCP流量控制2.1 滑动窗口&#xff08;Sliding Window&#xff09;2.2 拥塞控制&#xff08;Congestion C…...

python提取b站视频的音频(提供源码

如果我想开一家咖啡厅&#xff0c;那么咖啡厅的音乐可得精挑细选&#xff01;又假设我非常喜欢o叔&#xff0c;而o叔只在b站弹钢琴&#xff0c;那这时候我就得想方设法把b站的视频转为音频咯&#xff01; 一、首先打开网页版bilibili&#xff0c;按F12&#xff1a; 二、刷新页面…...

嵌入式Linux ,QT5 鼠标键盘设备参数指定环境变量的方法

根文件系统中&#xff0c;一般用mdev来管理设备&#xff0c;不像udev方便&#xff0c;有时候在执行rcS脚本的时候因为&#xff0c;太快&#xff0c;有些设备比如鼠标还没在/dev/input中生成设备文件&#xff0c;最好使用前用mdev -s扫描并等待几秒钟&#xff0c;然后就可以在in…...

C语言钥匙迷宫2.0

目录 开头程序程序的流程图程序游玩的效果结尾 开头 大家好&#xff0c;我叫这是我58。废话不多说&#xff0c;咱们直接开始。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <Windows.h> enum color {Y,B,R …...

【多线程】初步认识Thread类及其应用

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 上篇文章我们简单介绍了什么是进程与线程&#xff0c;以及他们之间的区别与联系&#xff0c;实际应用中还是以多线程编程为主的&#xff0c;所以这篇文章就让我们更加深入地去剖…...

algorithm算法库学习之——划分操作和排序操作

algorithm此头文件是算法库的一部分。本篇介绍划分操作和排序操作。 划分操作 is_partitioned (C11) 判断范围是否已按给定的谓词划分 (函数模板) partition 将范围中的元素分为两组 (函数模板) partition_copy (C11) 复制一个范围&#xff0c;将各元素分为两组 (函数模板) st…...

XSS实验记录

目录 XXS地址 实验过程 Ma Spaghet Jeff Ugandan Knuckles Ricardo Milos Ah Thats Hawt Ligma Mafia Ok, Boomer XXS地址 XSS Game - Learning XSS Made Simple! | Created by PwnFunction 实验过程 Ma Spaghet 要求我们弹出一个alert(1337)sandbox.pwnfuncti…...

Cortex-A7的GIC(全局中断控制器)使用方法(7):基于stm32MP135的GIC配置中断效果测试

0 参考资料 STM32MP13xx参考手册.pdf&#xff08;RM0475&#xff09; ARM Generic Interrupt Controller Architecture version 2.0 - Architecture Specification.pdf 1 GIC配置中断效果测试 前面我们已经实现了GIC的配置&#xff0c;为了验证GIC是否配置有效&#xff0c;本例…...

c++动态数组new和delete

文章目录 动态数组的使用大全1. **基本创建和初始化**2. **动态调整大小**3. **动态数组的使用与标准库 std::vector**4. **动态数组作为函数参数**输出 5. **使用动态数组存储用户输入** 动态数组的使用大全 1. 基本创建和初始化 示例&#xff1a; #include <iostream&g…...

Redis热点知识速览(redis的数据结构、高性能、持久化、主从复制、集群、缓存淘汰策略、事务、Pub/Sub、锁机制、常见问题等)

Redis是一个开源的、使用内存作为存储的、支持数据结构丰富的NoSQL数据库。它的高性能、灵活性和简单易用使其在许多场景下成为首选的缓存解决方案。以下是Redis的常见和热点知识总结。 数据结构 Redis支持五种基本数据结构&#xff1a; String&#xff1a;字符串是Redis中最…...

【C++浅析】lambda表达式:基本结构 使用示例

基本结构 [捕获列表](参数列表) -> 返回类型 { // 函数体 } 捕获列表 ([ ]): 用于指定外部变量的捕获方式。可以&#xff1a; 通过值捕获&#xff1a;[x]通过引用捕获&#xff1a;[&x]捕获所有变量通过值&#xff1a;[]捕获所有变量通过引用&#xff1a;[&]自…...

利用Redis获取权限的多种方式

更多实战内容&#xff0c;可前往无问社区查看http://www.wwlib.cn/index.php/artread/artid/10333.html Redis是我们在实战中经常接触到的一款数据库&#xff0c;因其在前期打点中被利用后可直接影响服务器安全所以在攻防过程中也备受红队关注&#xff0c;在本文中会重点分享一…...

LeetCode - LCR 146- 螺旋遍历二维数组

LCR 146题 题目描述&#xff1a; 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到提取完…...

如何获取Bing站长工具API密钥

Bing站长工具近期悄然上线了网站URL推送功能&#xff0c;似乎有意跟随百度的步伐。这个新功能允许站长通过API向Bing提交链接数据&#xff0c;当然也可以通过Bing站长工具手动提交。 本文将详细介绍如何通过Bing站长工具生成用于网站链接推送的API密钥。 首先&#xff0c;访问…...

NC 调整数组顺序使奇数位于偶数前面(一)

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 输入一个长度…...

Unity异步把图片数据从显存下载到内存(GPU->CPU)

Unity异步把图片数据从显存下载到内存&#xff08;GPU->CPU&#xff09; 1.c#核心代码 using System.Collections; using System.Collections.Generic; using Unity.Collections; using UnityEditor.PackageManager.Requests; using UnityEngine; using UnityEngine.Rende…...

【MySQL】C/C++连接MySQL客户端,MySQL函数接口认知,图形化界面进行连接

【MySQL】C/C引入MySQL客户端 安装mysqlclient库mysql接口介绍初始化mysql_init链接数据库mysql_real_connect下发mysql命令mysql_query获取出错信息mysql_error获取执行结果mysql_store_result获取结果行数mysql_num_rows获取结果列数mysql_num_fields判断结果列数mysql_field…...

Wireshark分析工具

简单用例 首先打开软件&#xff0c;左上角点文件&#xff0c;选中要分析的文件列表。 导入用tcpdump抓的包后进行分析&#xff0c;这里要输入过滤条件&#xff0c;对网络包进行一定的过滤处理。&#xff08;这里172网段是阿里云的地址&#xff0c;用自己写的python2脚本对阿里…...

linux网络配置脚本

通过脚本&#xff0c;设置静态ip以及主机名 因为企业9的网络配置文件和企业7的不一样所以&#xff0c;我们以rhel9和rhel7为例 rhel7/centos7/openeuler #!/bin/bash cat > /etc/sysconfig/network-scripts/ifcfg-$1 << EOF DEVICE$1 ONBOOTyes BOOTPROTOnone IPAD…...

IT管理:我与IT的故事4

首先&#xff0c;宣布一个“坏消息”。最近Herry童鞋的办公邮箱似乎有些“抽抽”了&#xff0c;所以邮件出现了延迟、拒收、被拒收、甚至是石沉大海的现象。为了能够更好的和大家进行沟通&#xff0c;大家如果发邮件到我办公邮箱的时候&#xff0c;若不嫌麻烦&#xff0c;可以抄…...

短链接系统设计方案

背景 需要设计一个短链接系统&#xff0c;主要功能主要有如下几点&#xff1a; ToB&#xff1a; 输入一个长链接&#xff0c;转换成短链接。这个短链接有时效性&#xff0c;可以设定指定过期时间。这个系统的每天会生成千万级别的短链接。数据具备可分析功能。 ToC&#xf…...

Cisco交换机SSH使用RSA公钥免密登录(IOS与Nexus,服务器以RHEL8为例)

目录 需求实验步骤0. 实验环境1. Linux2. CiscoIOS基础设置保存密钥登陆测试 3. CiscoNexus基础配置保存密钥登陆测试 需求 在实际工作中&#xff0c;常会遇到自动化的需求&#xff0c;那么在自动采集、配置等对网络设备的自动化需求中&#xff0c;不可避免的会遇到需要登录-&…...

QT判断操作系统类型和CPU架构

一、判断操作系统类型 1.在.pro文件中判断 macx { # mac only } unix:!macx{ # linux only } win32 { # windows only }2.在代码中判断 可以包含QGlobal头文件&#xff0c;判断预定义宏 #include <QtGlobal> ... #ifdef Q_OS_MAC // mac #endif#ifdef Q_OS_LINUX // …...

input[type=checkbox]勾选框自定义样式

效果图&#xff1a; <template> <input class"rule-checkbox" type"checkbox" checked v-model"isChecked" /> </template><script setup lang"ts"> import { ref } from vue; const isChecked ref(); </…...

鼠害监测系统:科技守护农业安全

在农业生产中&#xff0c;鼠害一直是威胁作物安全、影响产量的重要因素。然而&#xff0c;随着科技的飞速发展&#xff0c;鼠害监测系统正逐步成为现代农业防治鼠害的重要利器。 鼠害监测系统巧妙融合了现代光电、数控及物联网技术&#xff0c;实现了诱鼠、投喂鼠药、鼠情监测及…...

Ubuntu20.04如何安装配置JDK

资源准备 官方下载地址&#xff08;根据自己的系统版本选择不同版本进行下载即可&#xff09;&#xff1a;Java Downloads | Oracle 如无特殊需要可直接移步至下方JDK1.8安装包 https://download.csdn.net/download/qq_43439214/89646731 安装步骤 创建Java目录 sudo mkdir …...

Python3网络爬虫开发实战(9)代理的使用 (需补充代理池的构建)

文章目录 一、代理的设置1.1 urllib 的代理设置1.2 requests 的代理设置1.3 httpx 的代理设置1.4 aiohttp 的代理设置1.4 Selenium 的代理设置1.6 Playwright 的代理设置 二、代理池的构建和维护2.1 代理池的模块构成2.2 代理池的实现 网站为了避免爬虫采集数据可能会采取一些反…...

人际关系中的价值交换原理,在人类社会的复杂网络中,人际关系犹如一根根交织的丝线,将我们彼此紧密相连

人际关系中的价值交换原理,在人类社会的复杂网络中,人际关系犹如一根根交织的丝线,将我们彼此紧密相连。无论是亲情、友情还是爱情,这些关系在表面的情感纽带之下,实则都涉及到价值交换的原理。这种价值交换并非仅仅局限于物质层面,而是涵盖了情感、心理等人类所需的一切…...

西安电子科技大学萌新智慧指南(校区篇)

本次是西安电子科技大学南校区【本部南校区】 刚刚进入校园 相信大家对校园环境还很陌生 接下来就用一张地图 带大家迅速了解一下南校区的构造 宿舍 学生宿舍主要分为三部分 竹园公寓 1-4 海棠公寓 5-10 丁香公寓 11-15 研究生们主要居住在 海棠续建5、丁香14、丁香1…...

JavaScript基础(33)_鼠标滚轮滚动事件、键盘事件

鼠标滚轮滚动事件&#xff1a;onwheel 获取鼠标滚轮滚动的方向&#xff1a;wheelDelta 比如&#xff1a;向上滚动&#xff1a;109 &#xff08;所有正值都是向上&#xff09; 向下滚动&#xff1a;-109&#xff08;所有负值都是向下&#xff09; 注意&#xff1a;当…...

怎样做网站推广

拥有一个精致而富有吸引力的网站是成功商业运营的关键。然而&#xff0c;仅仅拥有一个网站是不够的&#xff0c;您还需要通过有效的推广策略吸引更多的访问者。以下是一些成功的网站推广策略&#xff0c;帮助您提升流量并增加知名度。 1. 优化SEO&#xff1a; 搜索引擎优化&am…...

Unity引擎加密方案解析

据悉&#xff0c;Unity引擎的全球市场占有率已经超过50%&#xff0c;而在全球排名前1000的手游当中&#xff0c;这一数据更是高达73%。不止如此&#xff0c;Unity在中国拥有高达350万的注册用户&#xff0c;《崩坏星穹铁道》、《王者荣耀》等爆款游戏均为Unity引擎开发。 庞大…...

遇到的几个iOS问题

1 unable to boot the simulator 跑模拟器的时候遇到这个报错&#xff0c; 解决方法 处理办法&#xff1a; 删除升级之前的模拟器缓存&#xff0c;重启模拟器。删除路径&#xff1a;~/Library/Developer/CoreSimulator/Cache 注意&#xff1a;后面可能还会复现这个报错&#x…...

掌握ChatGPT写作艺术:从入门到精通的四个层次

这些周末我仔细研究了如何通过优化提示词提升ChatGPT输出内容的质量。 关于如何使用ChatGPT辅助我们的写作&#xff0c;我归纳了以下规律&#xff0c;希望能为你带来启发。 一、写作步骤 撰写一篇文章&#xff0c;思路上必须是从抽象到具体逐步深入。 首先我们需要明确写什么…...

虚幻UE5安装报错误代码:SU-PQR5

找到图标的快捷方式 “Epic Games Launcher”右键属性&#xff0c;在目标最后添加-SkipBuildPatchPrereq&#xff0c;如下图&#xff1a; 最后&#xff0c;见证奇迹成功打开软件&#xff0c;可以继续安装啦。 参考资料&#xff1a; 【图片】求教各位大佬--错误代码SU-PQR5【ep…...

谷歌开源Gemma-2 百亿参数大模型,性能超越Llama-3模型,免费使用

Gemma 模型 Gemma模型是谷歌发布的一个开源模型&#xff0c;任何人都可以免费下载预训练模型&#xff0c;进行使用。而谷歌最近也发布了Gemma 2 模型&#xff0c;模型参数超过了 200 亿大官&#xff0c;果真大模型最后都是拼参数的时候吗。 Gemma 2 模型发布 Gemma 2 模型可以…...

人工智能与机器学习原理精解【12】

文章目录 分级聚类理论分级聚类的详细说明1. 定义2. 算法3. 计算4. 例子5. 例题 皮尔逊相关系数 julia实现 参考文献 分级聚类 理论 分级聚类的详细说明 1. 定义 分级聚类&#xff08;Hierarchical Clustering&#xff09;&#xff0c;又称为层次聚类&#xff0c;是一种通过…...

openEuler系统安装Visual Studio Code

openEuler系统安装Visual Studio Code 背景安装密钥和存储库更新包缓存并使用dnf安装包Fedora 22及以上版本旧版本使用yum 安装过程截图安装成功看桌面效果 背景 openEuler(openEuler-24.03-LTS)安装了麒麟UKUI桌面但是没有麒麟软件商店想安装Visual Studio Code 安装密钥和…...

Qt 系统相关 - 事件

目录 1. 事件介绍 2. 事件的处理 示例1&#xff1a;处理鼠标进入和离开 示例2&#xff1a;当鼠标点击时&#xff0c;获取对应的坐标值&#xff1b; 3. 按键事件 3.1 单个按键 3.2 组合按键 4. 鼠标事件 4.1 鼠标单击事件 4.2 鼠标释放事件 4.3 鼠标双击事件 4.4 鼠标…...