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

【CPP】死锁产生、排查、避免

一、死锁产生

死锁是指两个或多个线程互相等待对方释放资源,导致程序无法继续执行的现象。在多线程编程中,死锁是一种常见且严重的并发问题。死锁产生必须要四个条件同时满足才会发生

  • 互斥条件:某些资源只能由一个线程占用。
  • 占有且等待:线程已经占有至少一个资源,同时等待其他资源。
  • 不可剥夺:资源不能被强制剥夺,只能由持有线程主动释放。
  • 环路等待:多个线程形成环形等待链,导致死锁。

二、死锁排查

假如我们有如下代码需要排查死锁。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1;
std::mutex mtx2;void thread_func1() {std::lock_guard<std::mutex> lock1(mtx1); // 锁定 mtx1std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作std::lock_guard<std::mutex> lock2(mtx2); // 尝试锁定 mtx2
}void thread_func2() {std::lock_guard<std::mutex> lock1(mtx2); // 锁定 mtx2std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作std::lock_guard<std::mutex> lock2(mtx1); // 尝试锁定 mtx1
}int main() {std::thread t1(thread_func1);std::thread t2(thread_func2);t1.join();t2.join();return 0;
}

由于主线程的存在,在后续的表述中t1 对应线程2,t2 对应线程3

1)Linux命令

通过Linux命令初步排查判断死锁的发生。

1. ps

查看进程状态:ps -T -p <pid> -o pid,tid,user,stat,cmd

  • -T:显示指定进程的所有线程
  • -p <pid>:指定进程的 PID
  • -o:自定义输出的列,后面可以指定字段列表,用逗号分隔。
Ubuntu ❯❯❯ ps -T -p 2311810 -o pid,tid,user,stat,cmdPID     TID USER     STAT CMD
2311810 2311810 Raizero+ Sl+  ./deadlock
2311810 2311811 Raizero+ Sl+  ./deadlock
2311810 2311812 Raizero+ Sl+  ./deadlock
  • 线程状态(STAT):所有的线程都处于 S(Sleeping)状态。S 表示线程在等待某些操作(如 I/O、资源等),并且处于可中断的睡眠状态。此时线程可能处于的正常状态,而不是死锁的直接标志。死锁线程可能处于 SD 状态,需结合工具进一步分析。
2. top

实时监控线程和进程:top -H -p <pid>

  • -H:显示线程信息(Thread)
  • -p:指定进程的 PID(Process ID)
  • <pid>:可能发生死锁的进程pid
 进程号 USER      PR  NI    VIRT    RES    SHR    %CPU  %MEM     TIME+ COMMAND                                   
2292389 Raizero+  20   0   87992   2880   2880 S   0.0   0.0   0:00.00 deadlock                                  
2292390 Raizero+  20   0   87992   2880   2880 S   0.0   0.0   0:00.00 deadlock                                  
2292391 Raizero+  20   0   87992   2880   2880 S   0.0   0.0   0:00.00 deadlock     
  • 所有线程的 %CPU%MEM 都是 0.0,说明它们没有占用 CPU 资源,也没有执行任何工作。
  • 线程正在处于某种等待状态,而没有消耗 CPU 时间。死锁中,线程通常会持续等待并且不会执行任何任务,可能会表现出这种行为。

2)工具

1. gdb

使用 gdb 附加到正在运行的进程:gdb -p <pid>

  • -p:表示附加到正在运行的进程
  • <pid>:目标进程的进程 ID
Ubuntu ❯❯❯ gdb -p 2301629
...
__futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=2301630, futex_word=0x7c29d5a00910) at ./nptl/futex-internal.c:57
...

显示了一个关于 futex 的调用,futex 是一种用于线程同步的机制,通常与死锁或等待条件相关。这里的错误表明某个线程在等待某个 futex 锁,但 gdb 不能找到相关的源文件,因此它无法提供更多的调试信息。需要进一步调试:

  • 查看所有线程的堆栈:info threads

    (gdb) info threads
    ···
    Thread 0x7c29d62ac740 (LWP 2301629) "deadlock" 
    __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=2301630, futex_word=0x7c29d5a00910) 
    at ./nptl/futex-internal.c:57
    ···
    
    • 这表示线程 1 正在调用 futex 等待操作,可能是等待某个资源或锁的释放。
    ...
    Thread 0x7c29d5a00640 (LWP 2301630) "deadlock" 
    futex_wait (private=0, expected=2, futex_word=0x63974eebd1a0 <mtx2>) 
    at ../sysdeps/nptl/futex-internal.h:146
    ...
    
    • 线程 2 正在等待 mtx2 锁。
    ···
    Thread 0x7c29d5000640 (LWP 2301631) "deadlock" 
    futex_wait (private=0, expected=2, futex_word=0x63974eebd160 <mtx1>) 
    at ../sysdeps/nptl/futex-internal.h:146
    ···
    
    • 线程 3 正在等待 mtx1 锁。

    从这些信息可以推断出死锁的可能性。线程 2 正在等待 mtx2 锁,而线程 3 正在等待 mtx1 锁。考虑到这两个锁的互相依赖性,很可能发生了一个循环依赖(死锁):

    • 线程 1 可能持有 mtx1mtx2 锁,并且等待另一个锁。
    • 线程 2 等待 mtx2,而线程 3 等待 mtx1,它们互相等待对方释放锁,导致无法继续执行。
  • 检查线程堆栈:thread <thread> bt

    (gdb) thread 1 bt
    [Switching to thread 1 (Thread 0x7c29d62ac740 (LWP 2301629))]
    #0  __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=2301630, futex_word=0x7c29d5a00910) at ./nptl/futex-internal.c:57
    57      in ./nptl/futex-internal.c
    
    (gdb) thread 2 bt
    [Switching to thread 2 (Thread 0x7c29d5a00640 (LWP 2301630))]
    #0  futex_wait (private=0, expected=2, futex_word=0x63974eebd1a0 <mtx2>) at ../sysdeps/nptl/futex-internal.h:146
    
    (gdb) thread 3 bt
    [Switching to thread 3 (Thread 0x7c29d5000640 (LWP 2301631))]
    #0  futex_wait (private=0, expected=2, futex_word=0x63974eebd160 <mtx1>) at ../sysdeps/nptl/futex-internal.h:146
    
    • 线程 1 正在执行 __futex_abstimed_wait_common64,这表示它在等待某个资源或锁释放。
    • 线程 2线程 3 都在执行 futex_wait,分别等待 mtx2mtx1 锁的释放。

这些信息表明,线程 2 和线程 3 正在等待彼此持有的锁,这可能是死锁的典型表现。死锁的发生通常是由于线程形成了循环等待的依赖关系:

  • 线程 2 等待 mtx2 锁,而 mtx2 锁由线程 3 持有。
  • 线程 3 等待 mtx1 锁,而 mtx1 锁由线程 2 持有。
2. valgrind

使用其线程分析工具 Helgrind 检测死锁:valgrind --tool=helgrind <process_name>

Ubuntu ❯❯❯ valgrind --tool=helgrind ./a.out
  • 锁的首次获取路径

    • mtx1 (0x10E160) 首次被线程2获取

      ==2305017==  Lock at 0x10E160 was first observed
      ==2305017==    by 0x109365: thread_func1() (deadlock.cc:9)
      

      对应 thread_func1() 中第9行的 std::lock_guard<std::mutex> 锁定 mtx1

    • mtx2 (0x10E1A0) 首次被线程3获取

      ==2305017==  Lock at 0x10E1A0 was first observed
      ==2305017==    by 0x10946C: thread_func2() (deadlock.cc:17)
      

      对应 thread_func2() 中第17行的 std::lock_guard<std::mutex> 锁定 mtx2

  • 线程终止时的锁持有状态

    • 线程2(持有 mtx1)尝试获取 mtx2 时被阻塞

      ==2305017== Thread #2: Exiting thread still holds 1 lock
      ==2305017==    by 0x1093BA: thread_func1() (deadlock.cc:12)
      

      对应 thread_func1() 中第12行尝试获取 mtx2 时卡住(例如 std::lock_guard<std::mutex> lock2(mtx2))。

    • 线程3(持有 mtx2)尝试获取 mtx1 时被阻塞

      ==2305017== Thread #3: Exiting thread still holds 1 lock
      ==2305017==    by 0x1094C1: thread_func2() (deadlock.cc:20)
      

      对应 thread_func2() 中第20行尝试获取 mtx1 时卡住(例如 std::lock_guard<std::mutex> lock1(mtx1))。

根据以上信息说明两个线程在终止前未能释放已获得的锁,导致其他线程无法获取这些锁。Thread #2(假设对应代码中的线程1)在 thread_func1() 中先锁定 mtx1,然后尝试获取 mtx2Thread #3(假设对应代码中的线程2)在 thread_func2() 中先锁定 mtx2,然后尝试获取 mtx1。这种交叉锁定形成了循环等待,是死锁的典型条件。

3. 日志

在代码中加入日志记录,标注线程锁定和解锁的时间点、资源ID等信息,以分析死锁发生的位置。我们对于死锁代码加入如下日志:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1;
std::mutex mtx2;void thread_func1() {std::cout << "Thread 1: Trying to lock mtx1\n";std::lock_guard<std::mutex> lock1(mtx1); // 锁定 mtx1std::cout << "Thread 1: Acquired mtx1\n";std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作std::cout << "Thread 1: Trying to lock mtx2\n";std::lock_guard<std::mutex> lock2(mtx2); // 尝试锁定 mtx2std::cout << "Thread 1: Acquired mtx2 and completed\n";
}void thread_func2() {std::cout << "Thread 2: Trying to lock mtx2\n";std::lock_guard<std::mutex> lock1(mtx2); // 锁定 mtx2std::cout << "Thread 2: Acquired mtx2\n";std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作std::cout << "Thread 2: Trying to lock mtx1\n";std::lock_guard<std::mutex> lock2(mtx1); // 尝试锁定 mtx1std::cout << "Thread 2: Acquired mtx1 and completed\n";
}int main() {std::thread t1(thread_func1);std::thread t2(thread_func2);t1.join();t2.join();return 0;
}
  • 线程 1 停止在尝试锁定 mtx2:此时 mtx1 被线程 1 持有,而 mtx2 被线程 2 持有,两个线程互相等待解锁。

    Thread 2: Trying to lock mtx2
    Thread 2: Acquired mtx2
    Thread 2: Trying to lock mtx1
    Thread 1: Trying to lock mtx1
    Thread 1: Acquired mtx1
    Thread 1: Trying to lock mtx2
    
  • 线程 2 停止在尝试锁定 mtx1:情况相同,线程 1 和线程 2 分别持有一个锁,互相等待。

    Thread 1: Trying to lock mtx1
    Thread 1: Acquired mtx1
    Thread 1: Trying to lock mtx2
    Thread 2: Trying to lock mtx2
    Thread 2: Acquired mtx2
    Thread 2: Trying to lock mtx1
    

通过分析日志,如果发现某些线程的锁定日志输出到一半停止,后续锁未能成功获取且程序未继续运行,则可以推测程序可能发生了死锁。

三、死锁预防与解决

实际上来讲死锁几乎无法避免,我们只能说尽量避免并在死锁发生时利用各种手段解决。

1. 使用C++ RAII,避免忘记解锁造成的死锁

利用RAII(Resource Acquisition Is Initialization)机制,通过对象的构造和析构自动管理锁的生命周期,确保异常安全。

  • std::lock_guard

    std::mutex mtx;
    {std::lock_guard<std::mutex> lock(mtx); // 自动加锁// 临界区操作
    } // 离开作用域自动解锁
    
  • std::unique_lock

    std::mutex mtx;
    try {std::unique_lock<std::mutex> lock(mtx);// 可能抛出异常的操作
    } catch (...) {// 锁会在栈展开时自动释放
    }
    

优势

  • 避免因忘记调用unlock()导致的死锁。
  • 异常安全:即使临界区代码抛出异常,析构时仍会释放锁。
2. 使用c++ std::lock锁,来避免多次加锁顺序导致的死锁

std::lock(mtx1, mtx2, ...)原子性地同时锁定多个互斥量,避免因不同线程加锁顺序不一致导致的死锁。

std::mutex mtx1, mtx2;
{std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);std::lock(lock1, lock2); // 原子性同时加锁// 操作共享资源
} // 自动解锁

关键点

  • 结合std::adopt_lock标记表示锁已被获取,避免重复加锁。
  • 必须使用std::unique_lockstd::lock_guard不支持手动管理)。
3. 递归锁解决单线程重复加锁问题

使用std::recursive_mutex允许同一线程多次加锁。

std::recursive_mutex rmtx;
{std::lock_guard<std::recursive_mutex> lock1(rmtx); // 第一次加锁{std::lock_guard<std::recursive_mutex> lock2(rmtx); // 同一线程内再次加锁}
}

关键点

  • 递归锁性能低于普通锁,且需确保lock()unlock()次数匹配。
  • 优先考虑重构代码逻辑,避免嵌套加锁。
4. 避免嵌套锁

减小临界区范围仅在必须访问共享资源时加锁,或者将临界区代码提取为独立函数,减少锁的嵌套层次。

void critical_operation() {std::lock_guard<std::mutex> lock(mtx);// 仅包含必须同步的操作
}void outer_function() {// 非临界区代码critical_operation(); // 调用独立加锁的函数
}
5. 使用锁顺序

全局约定所有线程以相同顺序获取锁,破坏循环等待条件。

// 总是先锁地址最小的
void lock_in_order(std::mutex& mtx1, std::mutex& mtx2) {if (&mtx1 < &mtx2) {std::lock_guard<std::mutex> lock1(mtx1);std::lock_guard<std::mutex> lock2(mtx2);} else {std::lock_guard<std::mutex> lock2(mtx2);std::lock_guard<std::mutex> lock1(mtx1);}
}

按地址排序的锁顺序策略可能因平台或编译器差异失效。

6. 锁超时

使用try_lock_for()try_lock_until()设置超时。

std::timed_mutex tmtx;
if (tmtx.try_lock_for(std::chrono::milliseconds(100))) {// 成功获取锁tmtx.unlock();
} else {// 超时处理
}
7. 原子操作

使用原子变量彻底避免锁的使用,消除死锁风险。

std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed);
8. 检测并恢复
  • 动态检测:记录锁获取顺序,检测循环等待。
  • 超时回滚:设定超时时间,超时后释放资源并重试。
9. 数据分区

通过分区技术避免不同线程同时访问同一个资源,消除死锁可能性。

  • 将数据划分为多个独立的部分,分配给不同的线程。
  • 常用于多线程处理大规模数据的场景。

相关文章:

【CPP】死锁产生、排查、避免

一、死锁产生 死锁是指两个或多个线程互相等待对方释放资源&#xff0c;导致程序无法继续执行的现象。在多线程编程中&#xff0c;死锁是一种常见且严重的并发问题。死锁产生必须要四个条件同时满足才会发生&#xff1a; 互斥条件&#xff1a;某些资源只能由一个线程占用。占…...

深入理解 Android Handler

一、引言 Handler 在安卓中的地位是不言而喻的&#xff0c;几乎维系着整个安卓程序运行的生命周期&#xff0c;但是这么重要的一个东西&#xff0c;我们真的了解它吗&#xff1f;下面跟随着我的脚步&#xff0c;慢慢揭开Hanler的神秘面纱吧&#xff01; 本文将介绍Handler 的运…...

Git 进阶之路:高效协作之分支管理

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Git 企业级应用 目录 一&#xff1a;&#x1f525; 分⽀管理 &#x1f98b; 理解分⽀&#x1f98b; 创建分⽀&#x1f98b; 切换分⽀&#x1f98b; 合并分⽀&#x1f98b; 删除分⽀&#x1f98b; 合…...

LeetCode 2364.统计坏数对的数目:反向统计

【LetMeFly】2364.统计坏数对的数目&#xff1a;反向统计 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-number-of-bad-pairs/ 给你一个下标从 0 开始的整数数组 nums 。如果 i < j 且 j - i ! nums[j] - nums[i] &#xff0c;那么我们称 (i, j) 是一个 坏…...

6.Rust+Axum:打造高效 WebSocket 实时通信聊天室

摘要 本文详细介绍 RustAxum 在 WebSocket 实时通信开发中的应用&#xff0c;包括双向通信、状态管理等&#xff0c;实践构建聊天室应用。 一、引言 在当今的 Web 应用开发中&#xff0c;实时通信变得越来越重要。WebSocket 作为一种在单个 TCP 连接上进行全双工通信的协议&…...

【硬件系统架构】冯·诺依曼架构

一、引言 在计算机科学的广袤领域中&#xff0c;冯诺依曼架构犹如一颗璀璨的恒星&#xff0c;照亮了现代计算机发展的道路。从我们日常使用的个人电脑到强大的数据中心服务器&#xff0c;几乎都基于这一架构构建。它的出现是计算机发展史上的一个重要里程碑&#xff0c;深刻地影…...

Android 13 关闭屏幕调节音量大小

一、问题描述 在Android 13系统中,通过修改frameworks/base/core/res/res/values/config.xml配置文件,实现灭屏状态下调节音量的功能。 二、修改内容 diff --git a/frameworks/base/core/res/res/values/config.xml b/frameworks/base/core/res/res/values/config.xml inde…...

[编程基础] Java · 学习手册

&#x1f525; 《Java 工程师修炼之路&#xff1a;从零构建系统化知识体系》 &#x1f525; &#x1f6e0;️ 专栏简介&#xff1a; 这是一个以工业级开发标准打造的 Java 全栈技术专栏&#xff0c;涵盖语言核心、并发编程、JVM 原理、框架源码、架构设计等全维度知识体系。专…...

探索元生代:ComfyUI 工作流与计算机视觉的奇妙邂逅

目录 一、引言 二、蓝耘元生代和 ComfyUI 工作流初印象 &#xff08;一&#xff09;蓝耘元生代平台简介 &#xff08;二&#xff09;ComfyUI 工作流创建是啥玩意儿 三、计算机视觉是个啥 &#xff08;一&#xff09;计算机视觉的基本概念 &#xff08;二&#xff09;计算…...

C++ 迭代器失效详解:如何避免 vector 操作中的陷阱

目录 1. 什么是迭代器失效&#xff1f; 2. 哪些操作会导致迭代器失效&#xff1f; 2.1 vector 的插入操作&#xff08;push_back, insert&#xff09; 示例&#xff1a;push_back 导致迭代器失效 如何避免&#xff1f; 2.2 vector 的删除操作&#xff08;erase, pop_back&…...

【fisco bcos】基于ABI调用智能合约

参考官方文档&#xff1a;https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/sdk/java_sdk/assemble_transaction.html 先放一下智能合约&#xff1a; &#xff08;就是一个很简单的插入和查找嗯&#xff09; pragma solidity ^0.4.25; pragma experimental…...

【Python学习笔记】Pandas实现Excel质检记录表初审、复核及质检统计

背景&#xff1a; 我有这样一个需要审核的飞书题目表&#xff0c;按日期分成多个sheet&#xff0c;有初审——复核——质检三个环节&#xff0c;这三个环节是不同的同学在作业&#xff0c;并且领到同一个题目的人选是随机的&#xff0c;也就是说&#xff0c;完成一道题的三个人…...

Springboot 自动装配原理是什么?SPI 原理又是什么?

1. Spring Boot 自动装配原理 自动装配是 Spring Boot 简化配置的核心机制&#xff0c;其核心思想是根据类路径中的依赖自动配置 Spring 应用。 关键步骤&#xff1a; 启动注解 SpringBootApplication 该注解组合了 EnableAutoConfiguration&#xff0c;用于激活自动配置。 …...

【英语语法】基本句型

目录 前言一&#xff1a;主谓二&#xff1a;主谓宾三&#xff1a;主系表四&#xff1a;主谓双宾五&#xff1a;主谓宾补 前言 英语基本句型是语法体系的基石&#xff0c;以下是英语五大基本句型。 一&#xff1a;主谓 结构&#xff1a;主语 不及物动词 例句&#xff1a; T…...

扫雷-C语言版

C语言扫雷游戏设计&#xff08;完整版&#xff09; 游戏背景 扫雷是一款经典的益智类单人电脑游戏&#xff0c;最早出现在1960年代&#xff0c;并在1990年代随着Windows操作系统而广为人知。游戏目标是在不触发任何地雷的情况下&#xff0c;揭开所有非地雷的格子。玩家需要根…...

【C++初阶】--- list容器功能模拟实现

1.什么是list容器 在 C 标准模板库&#xff08;STL&#xff09;中&#xff0c;std::list 是一个非常有用的容器&#xff0c;它是双向链表的实现std::list 是一种序列式容器&#xff0c;它允许在序列内的任意位置进行高效的插入和删除操作。与数组和 std::vector 不同&#xff…...

gRPC 介绍及在嵌入式 Linux 下的成功编译及使用详解

gRPC 是一个高性能、开源和通用的 RPC 框架&#xff0c;由 Google 开发。它支持多种编程语言&#xff0c;并且能够运行在不同的环境中&#xff0c;包括嵌入式系统。本文将详细介绍 gRPC&#xff0c;以及如何在嵌入式 Linux 系统下成功编译 gRPC&#xff0c;并结合 protobuf 和 …...

C语言教程(十):C 语言函数详解

一、引言 在 C 语言中&#xff0c;函数是一组执行特定任务的代码块。通过将复杂的程序逻辑划分为多个函数&#xff0c;不仅能提高代码的可读性、可维护性&#xff0c;还便于代码的复用。无论是简单的数学计算&#xff0c;还是复杂的系统操作&#xff0c;函数都发挥着核心作用。…...

力扣刷题-热题100题-第35题(c++、python)

146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/lru-cache/?envTypestudy-plan-v2&envIdtop-100-liked 双向链表哈希表 内置函数 对于c有list可以充当双向链表&#xff0c;unordered_map充当哈希表&#xff1b;python有OrderedDic…...

LeetCode算法题(Go语言实现)_52

题目 给你一个下标从 0 开始的整数数组 costs &#xff0c;其中 costs[i] 是雇佣第 i 位工人的代价。 同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人&#xff1a; 总共进行 k 轮雇佣&#xff0c;且每一轮恰好雇佣一位工人。 在每一轮雇佣中&#xf…...

基于尚硅谷FreeRTOS视频笔记——13—HAL库和RTOS时钟源问题

RTOS的时钟源就是系统定时器中断&#xff0c;通俗来说就是系统定时器每中断一次&#xff0c;就扫描一次RTOS&#xff0c;查看RTOS中有没有任务的切换。 但是&#xff0c;系统存在一个HAL_Delay()函数&#xff0c;就是使用的系统定时器中断来执行的函数。 当我们在RTOS中&…...

FPGA入门学习Day1——设计一个DDS信号发生器

目录 一、DDS简介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要优势 &#xff08;三&#xff09;与传统技术的对比 二、FPGA存储器 &#xff08;一&#xff09;ROM波形存储器 &#xff08;二&#xff09;RAM随机存取存储器 &#xff08;三&…...

JavaScript-立即执行函数(Immediately Invoked Function Expression,IIFE)

立即执行函数&#xff08;Immediately Invoked Function Expression&#xff0c;IIFE&#xff09;是 JavaScript 里一种很独特的函数&#xff0c;它在定义后会马上执行。下面会详细介绍它的语法、用途、优点以及注意事项。 一、语法 立即执行函数一般有两种常见的语法形式&am…...

【Leetcode 每日一题 - 补卡】2537. 统计好子数组的数目

问题背景 给你一个整数数组 n u m s nums nums 和一个整数 k k k&#xff0c;请你返回 n u m s nums nums 中 好 子数组的数目。 一个子数组 a r r arr arr 如果有 至少 k k k 对下标 ( i , j ) (i, j) (i,j) 满足 i < j i < j i<j 且 a r r [ i ] a r r [ …...

【工具-Krillin AI】视频翻译、配音、语音克隆于一体的一站式视频多语言转换工具~

Krillin AI 是全能型音视频本地化与增强解决工具。这款简约而强大的工具&#xff0c;集音视频翻译、配音、语音克隆于一身&#xff0c;支持横竖屏格式输出&#xff0c;确保在所有主流平台&#xff08;哔哩哔哩&#xff0c;小红书&#xff0c;抖音&#xff0c;视频号&#xff0c…...

常用绑定事件方式有哪几种

绑定事件分为3种&#xff1a; 1、内联模式&#xff1a;将函数名直接作为标签属性的属性值&#xff08;注意&#xff1a;这里是带括号的,不带括号不生效&#xff0c;但是在vue中可以加括号也可以不加括号&#xff0c;如果需要穿参数就加括号&#xff0c;不需要传参数可以不加&am…...

数据结构之BFS广度优先算法(腐烂的苹果)

队列这个数据结构在很多场景下都有使用&#xff0c;比如在实现二叉树的层序遍历&#xff0c;floodfill问题(等等未完成)中&#xff0c;都需要借助队列的先进先出特性&#xff0c;下面给出这几个问题的解法 经典的二叉树的层序遍历 算法图示&#xff0c;以下图所示的二叉树为例…...

linux 学习 1.开始学习

准备学习linux记录一下学习内容&#xff0c;只会包含必要的知识&#xff0c;和部分演示 我采用的系统是Ubuntu24.04 初始掌握 学习首先需要掌握如何查看帮助手册 man man # man 加任何命令可以看具体命令的帮助手册 man mkdir进入手册按 d(down)&#xff1a;往下翻半页u(u…...

Flink-01学习 介绍Flink及上手小项目之词频统计

flink简介 官网 概述&#xff1a; 学习Flink具体包括四个关键概念&#xff1a;流数据的持续处理&#xff0c;事件时间&#xff0c;有状态流处理和状态快照。 Apache Flink 是一个开源的流处理框架&#xff0c;旨在处理批处理和实时数据处理&#xff0c;具有高吞吐量和低延迟的…...

【Linux我做主】探秘gcc/g++和动静态库

TOC Linux编译器gcc/g的使用 github地址 有梦想的电信狗 前言 在软件开发的世界中&#xff0c;编译器如同匠人的工具&#xff0c;将人类可读的代码转化为机器执行的指令。 对于Linux开发者而言&#xff0c;gcc和g是构建C/C程序的核心工具链&#xff0c;掌握它们的原理和使…...

工控系统前端设计(pyqt)

题目源自&#xff1a;白月黑羽的项目实战四-[工控系统前端] 代码已上传至gitcode https://gitcode.com/m0_37662818/Industrial_Control_System_Front_End 心得体会&#xff1a;直接用组态软件或者js吧 项目亮点 tablemodel的使用&#xff0c;绑定了表格和数据风机自定义ite…...

一台 Master 多节点玩转 Kubernetes:sealos 一键部署实践

文章目录 一台 Master 多节点玩转 Kubernetes&#xff1a;sealos 一键部署实践&#x1f517; 参考链接&#x1f310; 部署环境&#x1f4e6; 安装包说明&#x1f527; 前期准备&#x1f680; 使用 sealos 安装 Kubernetes✅ 验证集群状态&#x1f4cc; 后续可做的优化和拓展&am…...

写书的三驾马车

2019年8月19日23:52:28 先亮出我们的兵器组合&#xff1a; GitBook Git Markdown&#xff0c;享受行云流水一般的写作 个人秀 GitBook : 一个基于 Node.js 的文档格式转换工具&#xff0c;支持 Markdown 和 AsciiDoc 两种语法格式&#xff0c;可以输出 HTML、PDF等格式的…...

科学护理进行性核上性麻痹,缓解病痛提升生活质量

进行性核上性麻痹是一种罕见的神经系统变性疾病&#xff0c;患者常出现姿势平衡障碍、吞咽困难、眼球运动异常等症状。通过科学的健康护理&#xff0c;能在一定程度上减轻患者痛苦&#xff0c;提升生活质量。 日常护理&#xff0c;保障安全舒适 患者日常活动时&#xff0c;需确…...

第七章:7.2求方程a*x*x+b*x+c=0的根,用3个函数,分别求当:b*b-4*a*c大于0、等于0和小于0时的根并输出结果。从主函数输入a、b、c的值

//求方程a*x*xb*xc0的根&#xff0c;用3个函数&#xff0c;分别求当&#xff1a;b*b-4*a*c大于0、等于0和小于0时的根并输出结果。 //从主函数输入a、b、c的值 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<math.h> void s1(float a, float b, fl…...

优选算法系列(7.BFS 解决最短路问题)

简介&#xff1a; 先走到A&#xff0c;之后弹出A再把A能走到的地方加进去向外扩展把队列里面的元素&#xff08;B&#xff0c;C&#xff09;弹出来&#xff0c;再把B&#xff0c;C能到的地方入队列 一直这样那么最短路程就是扩展的层数。 迷宫中离入口最近的出口&#xff08;me…...

实现定时发送邮件,以及时间同步

定时发送邮件 部署邮件服务 查看有没有安装mailx,安装了 [root192 ~]# rpm -q mailx mailx-12.5-43.fc38.x86_64去网易拿一下授权码,写到配置文件里 vim /etc/mail.rcset fromxxxxxxx163.com set smtpsmtp.163.com set smtp-auth-userxxxxxxx163.com set smtp-auth-passwor…...

Java反射知识点学习笔记

目录 一、定义 二、获取class对象的三种方式 1、Class.forName("全类名") 2、类名.class 3、对象.getClass() 三、案例 1、获取 class 反射对象三种方式 2、利用反射获取构造方法 3、利用反射获取成员变量 4、利用反射获取成员方法 Java反射是一种强大的编…...

Unity ShaderLab引用HLSL文件找不到其中函数

在写Unity Shader的过程中&#xff0c;常常需要将方法封装到HLSL文件中&#xff0c;今天遇到一个这样的报错&#xff0c; 明明hlsl文件路径引用没问题&#xff0c;却引用不到方法 并且将分散文件中的函数复制过来一切正常&#xff0c;最终定位到HLSL的预编译指令中 这指令的…...

【文献笔记】LLM-based control code generation using image recognition

LLM-based control code generation using image recognition 原文代码 标题翻译&#xff1a;基于图像识别的LLM控制代码生成 1. 内容介绍 1.1. 简介 论文提出了一种基于LLM的新方法&#xff0c;通过图像识别从管道仪表图&#xff08;Piping and Instrumentation Diagrams,…...

算法之贪心算法

贪心算法 贪心算法核心思想常见应用场景典型案例案例一&#xff1a;找零问题案例二&#xff1a;活动选择问题案例三&#xff1a;货仓选址问题 贪心算法的应用详解霍夫曼编码最小生成树Dijkstra最短路径算法 总结 贪心算法 核心思想 贪心算法&#xff08;Greedy Algorithm&…...

从“链主”到“全链”:供应链数字化转型的底层逻辑

1. 制造业与供应链数字化转型的必然性 1.1. 核心概念与战略重要性 制造业的数字化转型&#xff0c;是利用新一代数字技术&#xff08;如工业互联网、人工智能、大数据、云计算、边缘计算等&#xff09;对制造业的整体价值链进行根本性重塑的过程。这不仅涉及技术的应用&#…...

【Windows本地部署n8n工作流自动平台结合内网穿透远程在线访问】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

Python中如何加密/解密敏感信息(如用户密码、token)

敏感信息,如用户密码、API密钥、访问令牌(token)、信用卡号以及其他个人身份信息(PII),构成了现代应用程序和系统中最为关键的部分。这些信息一旦被未经授权的第三方获取,可能引发灾难性的后果,从个人隐私泄露到企业经济损失,甚至是大规模的社会安全问题。保护这些敏感…...

Win10如何一键切换IP地址教程

切换IP地址可能对于许多用户来说是一个相对陌生但又可能经常需要进行的操作。无论是出于网络安全、突破网络限制还是仅仅为了测试目的&#xff0c;一键切换IP地址都能带来极大的便利。以下是在 Windows 10 中通过批处理脚本实现一键切换 IP 地址的详细教程&#xff1a; 方法一&…...

2021-11-09 C++三位数平方含有该数

缘由求解&#xff0c;运算函数&#xff0c;哪位大神教一下-编程语言-CSDN问答 void 三位数平方含有该数() {//缘由https://ask.csdn.net/questions/7560152?spm1005.2025.3001.5141int a 100, aa 1000, f 0;while (a < aa){f a*a;while (f > a)if ((f - a) % aa)f …...

高效检测书签网址,告别无效链接烦恼

软件介绍 你是否有过面对浏览器中满满的书签&#xff0c;却不知道哪些网址还“健在”&#xff0c;哪些已经“跑路”的烦恼&#xff1f;别担心&#xff0c;今天就给大家介绍一款神奇的小工具——“网址小卫士”。 检测轻松搞定 还在一个个手动检查书签网址的有效性吗&#xf…...

SpringBoot高校学生评教系统设计实现

概述 基于SpringBoot的高校学生评教系统项目&#xff0c;该系统包含了学生评教、教师管理等功能&#xff0c;适合作为JavaWeb学习项目。 主要内容 1. 学生功能模块 查看评教信息&#xff1a;可以查看学期、院系、任课教师、课程名称等信息评价打分功能&#xff1a;可以对课…...

代码随想录算法训练营第二十天

LeetCode题目: 39. 组合总和40. 组合总和 II131. 分割回文串2176. 统计数组中相等且可以被整除的数对(每日一题) 其他: 今日总结 往期打卡 39. 组合总和 跳转: 39. 组合总和 学习: 代码随想录公开讲解 问题: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 targ…...

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…...