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

常见的“锁”有哪些?

悲观锁

悲观锁认为在并发环境中,数据随时可能被其他线程修改,因此在访问数据之前会先加锁,以防止其他线程对数据进行修改。常见的悲观锁实现有:

1.互斥锁

原理:互斥锁是一种最基本的锁类型,同一时间只允许一个线程访问共享资源。当一个线程获取到互斥锁后,其他线程如果想要访问该资源,就必须等待锁被释放。

应用场景:适用于写操作频繁的场景,如数据库中的数据更新操作。在 C++ 中可以使用 std::mutex 来实现互斥锁,示例代码如下:

#include <iostream>
#include <mutex>
#include <thread>std::mutex mtx;
int sharedResource = 0;void increment() {std::lock_guard<std::mutex> lock(mtx);sharedResource++;
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Shared resource: " << sharedResource << std::endl;return 0;
}

2.读写锁

原理:读写锁允许多个线程同时进行读操作,但在进行写操作时,会独占资源,不允许其他线程进行读或写操作。读写锁分为读锁和写锁,多个线程可以同时获取读锁,但写锁是排他的。

应用场景:适用于读多写少的场景,如缓存系统。在 C++ 中可以使用 std::shared_mutex 来实现读写锁,示例代码如下: 

#include <iostream>
#include <shared_mutex>
#include <thread>std::shared_mutex rwMutex;
int sharedData = 0;void readData() {std::shared_lock<std::shared_mutex> lock(rwMutex);std::cout << "Read data: " << sharedData << std::endl;
}void writeData() {std::unique_lock<std::shared_mutex> lock(rwMutex);sharedData++;std::cout << "Write data: " << sharedData << std::endl;
}int main() {std::thread t1(readData);std::thread t2(writeData);t1.join();t2.join();return 0;
}

    乐观锁

    乐观锁是一种在多线程环境中避免阻塞的同步技术,它假设大部分操作是不会发生冲突的,因此在操作数据时不会直接加锁,而是通过检查数据是否发生了变化来决定是否提交。如果在提交数据时发现数据已被其他线程修改,则会放弃当前操作,重新读取数据并重试。

    应用场景:适用于读多写少、冲突较少的场景,如电商系统中的库存管理。

    在 C++ 中,乐观锁的实现通常依赖于版本号时间戳的机制。每个线程在操作数据时,会记录数据的版本或时间戳,操作完成后再通过比较版本号或时间戳来判断是否发生了冲突。

    下面是一个使用版本号实现乐观锁的简单示例代码:

    #include <iostream>
    #include <thread>
    #include <atomic>
    #include <chrono>// 共享数据结构
    struct SharedData {int value;            // 数据的实际值std::atomic<int> version; // 数据的版本号,用于检查是否发生了修改
    };// 线程安全的乐观锁实现
    bool optimisticLockUpdate(SharedData& data, int expectedVersion, int newValue) {// 检查数据的版本号是否与预期一致if (data.version.load() == expectedVersion) {// 进行数据更新data.value = newValue;// 增加版本号data.version.fetch_add(1, std::memory_order_relaxed);return true; // 成功提交更新}return false; // 数据版本不一致,操作失败
    }void threadFunction(SharedData& data, int threadId) {int expectedVersion = data.version.load();int newValue = threadId * 10;std::cout << "Thread " << threadId << " starting with version " << expectedVersion << "...\n";std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作// 尝试更新数据if (optimisticLockUpdate(data, expectedVersion, newValue)) {std::cout << "Thread " << threadId << " successfully updated value to " << newValue << "\n";} else {std::cout << "Thread " << threadId << " failed to update (version mismatch)\n";}
    }int main() {// 初始化共享数据,值为 0,版本号为 0SharedData data{0, 0};// 启动多个线程进行乐观锁测试std::thread t1(threadFunction, std::ref(data), 1);//std::ref(data) 将 data 包装成一个引用包装器,确保 data 在传递给函数时以引用的方式传递,而不是被复制。std::thread t2(threadFunction, std::ref(data), 2);std::thread t3(threadFunction, std::ref(data), 3);t1.join();t2.join();t3.join();std::cout << "Final value: " << data.value << ", Final version: " << data.version.load() << "\n";return 0;
    }

    原子锁

    原子锁是一种基于原子操作(如CAS、test_and_set)的锁机制。与传统的基于互斥量(如 std::mutex)的锁不同,原子锁依赖于硬件提供的原子操作,允许对共享资源的访问进行同步,且通常比传统锁更加高效。它通过原子操作保证对共享资源的独占访问,而不需要显式的线程调度。

    原子锁的适用场景:

    1.简单数据类型:原子锁最常用于锁定简单的基础数据类型,例如整数、布尔值、指针等。通过原子操作,多个线程可以安全地对这些数据进行读写,而不会发生数据竞争。

    示例:std::atomic<int>, std::atomic<bool>, std::atomic<long long>

    2.计数器、标志位:当需要在多线程中维护计数器、标志位或状态变量时,原子操作非常合适。例如,当多个线程需要递增计数器时,可以用原子操作避免使用传统的互斥锁。

    示例:使用 std::atomic<int> 来维护线程安全的计数器。

    注:原子锁通常不能锁容器类型。

    什么是原子操作?

    原子操作是指不可分割的操作,在执行过程中不会被中断或干扰。原子操作保证了操作的完整性,要么完全执行,要么完全不执行,避免了在操作过程中被线程切换打断,从而避免了数据竞争和不一致的情况。

    1.自旋锁

    什么是自旋锁?

    自旋锁是一种使用原子操作来检测锁是否可用的锁机制。自旋锁是一种忙等待的锁,当线程尝试获取锁失败时,会不断地检查锁的状态,直到成功获取锁。 

    在 C++ 中,可以使用 std::atomic_flag 结合 test_and_set 操作来实现一个简单的自旋锁:

    test_and_set 是一个原子操作,它会检查一个布尔标志的值,然后将该标志设置为 true。整个操作过程是不可分割的,即不会被其他线程的操作打断。这个布尔标志通常被用作锁,线程通过检查并设置这个标志来尝试获取锁。

    工作原理

    • 检查标志状态:线程首先检查布尔标志的当前值。
    • 设置标志为 true:如果标志当前为 false,表示锁未被占用,线程将标志设置为 true,表示成功获取到锁;如果标志当前为 true,表示锁已被其他线程占用,线程未能获取到锁。
    • 返回旧值:test_and_set 操作会返回标志的旧值。线程可以根据这个返回值判断是否成功获取到锁。如果返回 false,说明成功获取到锁;如果返回 true,则需要等待锁被释放后再次尝试获取。
    #include <iostream>
    #include <atomic>
    #include <thread>
    #include <vector>std::atomic_flag lock = ATOMIC_FLAG_INIT;// 自旋锁类
    class SpinLock {
    public:void lock() {// 持续尝试获取锁,直到成功while (lock.test_and_set(std::memory_order_acquire)) {// 自旋等待}}void unlock() {// 释放锁,将标志设置为 falselock.clear(std::memory_order_release);}
    };SpinLock spinLock;
    int sharedResource = 0;// 线程函数
    void worker() {for (int i = 0; i < 100000; ++i) {spinLock.lock();++sharedResource;spinLock.unlock();}
    }int main() {std::vector<std::thread> threads;// 创建多个线程for (int i = 0; i < 4; ++i) {threads.emplace_back(worker);}// 等待所有线程完成for (auto& thread : threads) {thread.join();}std::cout << "Shared resource value: " << sharedResource << std::endl;return 0;
    }

    自旋锁优点:

    1. 无上下文切换:自旋锁不会引起线程挂起,因此避免了上下文切换的开销。在锁竞争较轻时,自旋锁可以高效地工作。

    2. 简单高效:实现简单,且不依赖操作系统调度,适合锁竞争不严重的场景。

    自旋锁缺点:

    1. CPU资源浪费:如果锁被占用,自旋锁会不断地循环检查锁的状态,浪费 CPU 时间,尤其是在锁持有时间较长时,可能导致性能问题。

    2. 不适合锁竞争场景:当有大量线程竞争同一个锁时,自旋锁的性能将大幅下降,因为大部分时间都在自旋,浪费了 CPU 资源。

    自旋锁的适用场景:

    1. 短时间锁竞争:自旋锁适用于临界区代码执行时间非常短的情况。如果锁持有时间较长,使用自旋锁就不合适了。

    2. 锁竞争较轻:在多线程程序中,如果线程数量较少且资源竞争较少,自旋锁可以有效减少线程上下文切换,提升性能。

    3. 实时系统或高性能系统:在某些对延迟非常敏感的应用场景中,自旋锁可以通过减少上下文切换来提供更低的延迟。

    总结:自旋锁是一种简单且高效的锁机制,通过原子操作避免了线程上下文切换,适合用于短时间锁竞争和低延迟要求的场景。在锁竞争激烈或锁持有时间较长时,自旋锁的性能会受到影响,这时传统的互斥锁(如 std::mutex)可能更为合适。

    递归锁

    在 C++ 中,递归锁也被称为可重入锁,它是一种特殊的锁机制,允许同一个线程多次获取同一把锁而不会产生死锁。

    原理

    普通的互斥锁(如 std::mutex)不允许同一个线程在已经持有锁的情况下再次获取该锁,否则会导致死锁。因为当线程第一次获取锁后,锁处于被占用状态,再次尝试获取时,由于锁未被释放,线程会被阻塞,而该线程又因为被阻塞无法释放锁,从而陷入死循环。

    递归锁则不同,它内部维护了一个计数器和一个持有锁的线程标识。当一个线程第一次获取递归锁时,计数器加 1,同时记录该线程的标识。如果该线程再次请求获取同一把锁,计数器会继续加 1,而不会被阻塞。当线程释放锁时,计数器减 1,直到计数器为 0 时,锁才会真正被释放,其他线程才可以获取该锁。

    应用场景:

    • 递归调用:在递归函数中,如果需要对共享资源进行保护,使用递归锁可以避免死锁问题。例如,在一个递归遍历树结构的函数中,可能需要对树节点的某些属性进行修改,此时可以使用递归锁来保证线程安全。
    • 嵌套锁:当代码中存在多层嵌套的锁获取操作,且这些操作可能由同一个线程执行时,递归锁可以避免死锁。例如,一个函数内部调用了另一个函数,这两个函数都需要获取同一把锁。

    注意事项:

    1. 性能开销

    递归锁的实现比普通互斥锁更为复杂。普通互斥锁只需简单地标记锁的占用状态,当一个线程请求锁时,检查该状态并进行相应操作。而递归锁除了要维护锁的占用状态,还需要记录持有锁的线程标识以及一个计数器,用于跟踪同一个线程获取锁的次数。每次获取和释放锁时,都需要对这些额外信息进行更新和检查,这无疑增加了系统的开销。

    • 时间开销:由于额外的状态检查和更新操作,递归锁的加锁和解锁操作通常比普通互斥锁更耗时。在高并发、对性能要求极高的场景下,频繁使用递归锁可能会成为性能瓶颈。
    • 资源开销:记录线程标识和计数器需要额外的内存空间,虽然这部分开销相对较小,但在资源受限的系统中,也可能会产生一定的影响。

    建议:在不需要递归获取锁的场景下,应优先使用普通互斥锁(如 std::mutex)。

    2. 死锁风险

    虽然递归锁允许同一个线程多次获取同一把锁而不会死锁,但如果在递归调用过程中,锁的获取和释放逻辑出现错误,仍然可能导致死锁。例如,在递归函数中,获取锁后在某些条件下没有正确释放锁就进行了递归调用,可能会导致锁无法正常释放,其他线程请求该锁时就会陷入死锁。

    #include <iostream>
    #include <thread>
    #include <mutex>std::recursive_mutex recMutex;void faultyRecursiveFunction(int n) {if (n == 0) return;std::lock_guard<std::recursive_mutex> lock(recMutex);std::cout << "Recursive call: " << n << std::endl;if (n == 2) {// 错误:没有释放锁就返回,可能导致死锁return;}faultyRecursiveFunction(n - 1);
    }int main() {std::thread t(faultyRecursiveFunction, 3);t.join();return 0;
    }
    

    3.不同递归锁之间的交叉锁定

    当存在多个递归锁时,如果不同线程以不同的顺序获取这些锁,就可能会产生死锁。例如,线程 A 先获取了递归锁 L1,然后尝试获取递归锁 L2;而线程 B 先获取了递归锁 L2,然后尝试获取递归锁 L1。此时,两个线程都在等待对方释放锁,从而陷入死锁状态。

    在 C++ 标准库中,std::recursive_mutex 是递归锁的实现。以下是一个简单的示例代码:

    #include <iostream>
    #include <thread>
    #include <mutex>std::recursive_mutex recMutex;// 递归函数,多次获取递归锁
    void recursiveFunction(int n) {if (n == 0) return;// 加锁std::lock_guard<std::recursive_mutex> lock(recMutex);std::cout << "Recursive call: " << n << std::endl;// 递归调用recursiveFunction(n - 1);// 锁在离开作用域时自动释放
    }int main() {std::thread t(recursiveFunction, 3);t.join();return 0;
    }
    

    什么是锁的重入与不可重入?

    可重入锁也叫递归锁,允许同一个线程在已经持有该锁的情况下,再次获取同一把锁而不会产生死锁。可重入锁内部会维护一个持有锁的线程标识和一个计数器。当线程第一次获取锁时,会记录该线程的标识,并将计数器初始化为 1。如果该线程再次请求获取同一把锁,锁会检查请求线程的标识是否与当前持有锁的线程标识相同,如果相同,则将计数器加 1,而不会阻塞该线程。释放锁时,计数器减 1,直到计数器为 0 时,锁才会释放,其他线程才可以获取该锁。

    不可重入锁不允许同一个线程在已经持有该锁的情况下再次获取同一把锁。如果一个线程已经持有了不可重入锁,再次请求获取该锁时,会导致线程阻塞,进而可能产生死锁。不可重入锁只关注锁的占用状态,不记录持有锁的线程标识和获取锁的次数。当一个线程请求获取锁时,锁会检查其是否已被占用,如果已被占用,无论请求线程是否就是持有锁的线程,都会将该线程阻塞。

    相关文章:

    常见的“锁”有哪些?

    悲观锁 悲观锁认为在并发环境中&#xff0c;数据随时可能被其他线程修改&#xff0c;因此在访问数据之前会先加锁&#xff0c;以防止其他线程对数据进行修改。常见的悲观锁实现有&#xff1a; 1.互斥锁 原理&#xff1a;互斥锁是一种最基本的锁类型&#xff0c;同一时间只允…...

    二级公共基础之数据库设计基础(一) 数据库系统的基本概念

    目录 ​​​​​​​前言 一、数据库、数据管理系统和数据库系统 1.数据 2.数据库 3.数据库管理系统 1.数据库管理系统的定义 2.数据库管理系统的功能 1.数据定义功能 2.数据操作功能 3.数据存取控制 4.数据完整性管理 5.数据备份和恢复 6.并发控制 4.数…...

    ollama无法通过IP:11434访问

    目录 1.介绍 2.直接在ollama的当前命令窗口中修改&#xff08;法1&#xff09; 3.更改ollama配置文件&#xff08;法2&#xff09; 3.1更新配置 3.2重启服务 1.介绍 ollama下载后默认情况下都是直接在本地的11434端口中运行&#xff0c;绑定到127.0.0.1(localhost)&#x…...

    简单易懂,解析Go语言中的struct结构体

    目录 4. struct 结构体4.1 初始化4.2 内嵌字段4.3 可见性4.4 方法与函数4.4.1 区别4.4.2 闭包 4.5 Tag 字段标签4.5.1定义4.5.2 Tag规范4.5.3 Tag意义 4. struct 结构体 go的结构体类似于其他语言中的class&#xff0c;主要区别就是go的结构体没有继承这一概念&#xff0c;但可…...

    java给钉钉邮箱发送邮件

    1.开通POP和IMAP 2.引入pom <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version> </dependency>3.逻辑 String host "smtp.qiye.aliyun.com"; String port "…...

    C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化

    欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 1、本节实现的内容 上一节课,我们在Blend软件中导出经纬球模型时,遇到了经纬球法线导致我们在游戏中模型光照显示问题,我们在Blender软件中可以通过…...

    大连本地知识库的搭建--数据收集与预处理_01

    1.马蜂窝爬虫 编程语言&#xff1a;Python爬虫框架&#xff1a;Selenium&#xff08;用于浏览器自动化&#xff09;解析库&#xff1a;BeautifulSoup&#xff08;用于解析HTML&#xff09; 2.爬虫策略 目标网站&#xff1a;马蜂窝&#xff08;https://www.mafengwo.cn/&…...

    github 推送的常见问题以及解决

    文章目录 git add 的时候问题1为什么会发生这种情况&#xff1f;Git 的警告含义如何解决&#xff1f;1. **保持 Git 的默认行为&#xff08;推荐&#xff09;**2. **禁用自动转换**3. **仅在工作目录中禁用转换**4. **统一使用 LF&#xff08;跨平台开发推荐&#xff09;** git…...

    stm32单片机个人学习笔记16(SPI通信协议)

    前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…...

    Linux | RHEL / CentOS 中 YUM history / downgrade 命令回滚操作

    注&#xff1a;英文引文&#xff0c;机翻未校。 在 RHEL/CentOS 系统上使用 YUM history 命令回滚升级操作 作者&#xff1a; 2daygeek 译者&#xff1a; LCTT DarkSun 为服务器打补丁是 Linux 系统管理员的一项重要任务&#xff0c;为的是让系统更加稳定&#xff0c;性能更加…...

    BGP状态和机制

    BGP邻居优化 为了增加稳定性,通常建议实验回环口来建立邻居。更新源:建立邻居和邻居所学习到的路由的下一跳。多跳:EBGP邻居建立默认选哟直连,因为TTL=1,如果非直连,必须修改TTL。命令备注peer 2.2.2.2 connect-interface lo1配置更新源peer 2.2.2.2 ebgp-max-hop 2配置T…...

    温湿度监控设备融入智慧物联网

    当医院的温湿度监控设备融入智慧物联网&#xff0c;将会带来许多新的体验&#xff0c;可以帮助医院温湿度监控设备智能化管理&#xff0c;实现设备之间的互联互通&#xff0c;方便医院对温湿度数据进行统一管理和分析。 添加智慧物联网技术&#xff0c;实现对医院温湿度的实时…...

    smolagents学习笔记系列(五)Tools-in-depth-guide

    这篇文章锁定官网教程中的 Tools-in-depth-guide 章节&#xff0c;主要介绍了如何详细构造自己的Tools&#xff0c;在之前的博文 smolagents学习笔记系列&#xff08;二&#xff09;Agents - Guided tour 中我初步介绍了下如何将一个函数或一个类声明成 smolagents 的工具&…...

    前端面试真题 2025最新版

    文章目录 写在前文CSS怪异盒模型JS闭包闭包的形成闭包注意点 CSS选择器及优先级优先级 说说flex布局及相关属性Flex 容器相关属性&#xff1a;Flex 项目相关属性 响应式布局如何实现是否用过tailwindcss&#xff0c;有哪些好处好处缺点 说说对象的 prototype属性及原型说说 pro…...

    面试八股文--数据库基础知识总结(1)

    1、数据库的定义 数据库&#xff08;DataBase&#xff0c;DB&#xff09;简单来说就是数据的集合数据库管理系统&#xff08;Database Management System&#xff0c;DBMS&#xff09;是一种操纵和管理数据库的大型软件&#xff0c;通常用于建立、使用和维护数据库。数据库系统…...

    10. docker nginx官方镜像使用方法

    本文介绍docker nginx官方镜像使用方法&#xff0c;因为第一次用&#xff0c;在加上对docker也不是很熟&#xff0c;中间踩了一些坑&#xff0c;为了避免下一次用又踩坑&#xff0c;因此记录如下&#xff0c;也希望能够帮到其它小伙伴。 官方镜像页面&#xff1a;https://hub.d…...

    [Web 安全] PHP 反序列化漏洞 —— PHP 反序列化漏洞演示案例

    关注这个专栏的其他相关笔记&#xff1a;[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 PHP 反序列化漏洞产生原因 PHP 反序列化漏洞产生的原因就是因为在反序列化过程中&#xff0c;unserialize() 接收的值可控。 0x01&#xff1a;环境搭建 这里笔者是使用 PhpStudy 搭建的环…...

    es-head(es库-谷歌浏览器插件)

    1.下载es-head插件压缩包&#xff0c;并解压缩 2.谷歌浏览器添加插件 3.使用...

    第二十:【路由的props配置】

    作用&#xff1a;让路由组件更方便的收到参数&#xff08;可以将路由参数作为props传给组件&#xff09; {name:xiang,path:detail/:id/:title/:content,component:Detail, ​ 第一种方法&#xff1a;// props的对象写法&#xff0c;作用&#xff1a;把对象中的每一组key-valu…...

    Vue 2全屏滚动动画实战:结合fullpage-vue与animate.css打造炫酷H5页面

    引言 在移动端H5开发中&#xff0c;全屏滚动效果因其沉浸式体验而广受欢迎。如何快速实现带有动态加载动画的全屏滚动页面&#xff1f;本文将手把手教你使用 Vue 2、全屏滚动插件 fullpage-vue 和动画库 animate.css 3.5.1&#xff0c;打造一个高效且视觉冲击力强的H5页面。通…...

    DAMOYOLO-S多场景落地:智能硬件产品出厂前目标检测功能自动化校验

    DAMOYOLO-S多场景落地&#xff1a;智能硬件产品出厂前目标检测功能自动化校验 1. 引言&#xff1a;从质检痛点说起 想象一下这个场景&#xff1a;你是一家智能硬件公司的生产线负责人。每天&#xff0c;成千上万的摄像头、扫地机器人、智能门锁从流水线上下来。每个产品都内置…...

    如何突破数字图书馆借阅限制:Internet Archive Downloader技术深度解析

    如何突破数字图书馆借阅限制&#xff1a;Internet Archive Downloader技术深度解析 【免费下载链接】internet_archive_downloader A chrome/firefox extension that download books from Internet Archive(archive.org) and HathiTrust Digital Library (hathitrust.org) 项…...

    Qwen3-VL-WEBUI作品集:看AI如何理解复杂图片与长视频内容

    Qwen3-VL-WEBUI作品集&#xff1a;看AI如何理解复杂图片与长视频内容 1. 引言&#xff1a;当AI开始"看懂"世界 想象一下&#xff0c;你给AI看一张满是文字的截图&#xff0c;它不仅能告诉你上面写了什么&#xff0c;还能分析出哪个是登录按钮、哪个是搜索框&#x…...

    仲景中医大语言模型:当千年中医智慧遇见现代人工智能

    仲景中医大语言模型&#xff1a;当千年中医智慧遇见现代人工智能 【免费下载链接】CMLM-ZhongJing 首个中医大语言模型——“仲景”。受古代中医学巨匠张仲景深邃智慧启迪&#xff0c;专为传统中医领域打造的预训练大语言模型。 The first-ever Traditional Chinese Medicine l…...

    解锁Android的Linux潜能:PRoot如何重塑移动开发边界

    解锁Android的Linux潜能&#xff1a;PRoot如何重塑移动开发边界 【免费下载链接】proot An chroot-like implementation using ptrace. 项目地址: https://gitcode.com/gh_mirrors/pro/proot 在移动设备上运行完整的Linux环境曾经是遥不可及的梦想&#xff0c;需要复杂的…...

    Lean 4终极指南:从定理证明到函数式编程的完整教程

    Lean 4终极指南&#xff1a;从定理证明到函数式编程的完整教程 【免费下载链接】lean4 Lean 4 programming language and theorem prover 项目地址: https://gitcode.com/GitHub_Trending/le/lean4 Lean 4作为微软研究院开发的函数式编程语言和定理证明器&#xff0c;近…...

    5分钟搞定YOLOv10部署:为什么这个方案最省心?

    5分钟搞定YOLOv10部署&#xff1a;为什么这个方案最省心&#xff1f; 【免费下载链接】yolov10 YOLOv10: Real-Time End-to-End Object Detection [NeurIPS 2024] 项目地址: https://gitcode.com/GitHub_Trending/yo/yolov10 还在为深度学习框架的环境配置抓狂&#xf…...

    AIAgent如何实现类人推理?:从符号逻辑到神经符号融合的5层知识表示架构实战解析

    第一章&#xff1a;AIAgent如何实现类人推理&#xff1f; 2026奇点智能技术大会(https://ml-summit.org) 类人推理并非简单地堆叠参数或延长上下文窗口&#xff0c;而是通过分层认知架构模拟人类“思考—质疑—修正—决策”的闭环过程。现代AIAgent借助多阶段推理链&#xff0…...

    3分钟搞定!WinCDEmu免费虚拟光驱终极指南:告别实体光盘的时代

    3分钟搞定&#xff01;WinCDEmu免费虚拟光驱终极指南&#xff1a;告别实体光盘的时代 【免费下载链接】WinCDEmu 项目地址: https://gitcode.com/gh_mirrors/wi/WinCDEmu 还在为找不到光驱而烦恼吗&#xff1f;还在为ISO文件打不开而困扰吗&#xff1f;今天我要向你介绍…...

    影墨·今颜集成微信小程序开发:打造个性化AI绘画工具

    影墨今颜集成微信小程序开发&#xff1a;打造个性化AI绘画工具 想不想让用户动动手指&#xff0c;就能在微信里把脑海中的画面变成一幅画&#xff1f;或者上传一张照片&#xff0c;就能生成一张风格独特的艺术头像&#xff1f;这听起来像是未来应用&#xff0c;但其实用我们今…...