线程同步与互斥---线程互斥
目录程线程间的互斥相关背景概念1.线程互斥1.见一种现象(数据不一致问题)2.解决这个问题(锁pthread锁)3.理解为什么数据会不一致认识加锁的接口4.理解锁线程是共享地址空间的---线程会共享大部分资源---那么在多线程访问这个共享资源时我们把共享资源叫做公共资源各种情况的数据不一致问题---解决这些问题同步和互斥程线程间的互斥相关背景概念• 共享资源• 临界资源多线程执行流被保护的共享的资源就叫做临界资源• 临界区每个线程内部访问临界资源的代码就叫做临界区• 互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起 保护作用• 原子性后面讨论如何实现不会被任何调度机制打断的操作该操作只有两态要么完成 要么未完成1.线程互斥1.见一种现象(数据不一致问题)TestMutex.cc// 操作共享变量会有问题的售票系统代码 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h int ticket 1000; void *route(void *arg) { char *id (char *)arg; while (1) { if (ticket 0) { usleep(1000); //模拟抢票花的时间 printf(%s sells ticket:%d\n, id, ticket); //抢到了票 ticket--; //票数-- } else { break; } } return nullptr; } int main(void) { pthread_t t1, t2, t3, t4; pthread_create(t1, NULL, route, (void *)thread 1); pthread_create(t2, NULL, route, (void *)thread 2); pthread_create(t3, NULL, route, (void *)thread 3); pthread_create(t4, NULL, route, (void *)thread 4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); }MakefileTestMutex:TestMutex.cc g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f TestMutexrootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27/5.mutex# make g -o TestMutex TestMutex.cc -stdc11 -lpthread rootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27/5.mutex# ./TestMutex thread 2 sells ticket:1000 thread 1 sells ticket:1000 thread 3 sells ticket:1000 thread 4 sells ticket:1000 thread 2 sells ticket:996 thread 1 sells ticket:995 thread 3 sells ticket:995 ....... thread 3 sells ticket:10 thread 4 sells ticket:9 thread 2 sells ticket:8 thread 1 sells ticket:7 thread 3 sells ticket:6 thread 4 sells ticket:5 thread 2 sells ticket:4 thread 1 sells ticket:3 thread 3 sells ticket:2 thread 4 sells ticket:1 thread 2 sells ticket:0 thread 1 sells ticket:-1 thread 3 sells ticket:-2抢票抢到负数要把临界区保护起来2.解决这个问题(锁pthread锁)pthread_mutex_init 初始化互斥锁NAMEpthread_mutex_destroy,pthread_mutex_init - destrobyand initialize a mutexSYNOPSIS#include pthread.hint pthread_mutex_destroy(pthread_mutex_t *mutex);int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock//加锁pthread_mutex_unlock//解锁TestMutex.cc// 操作共享变量会有问题的售票系统代码 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h int ticket 1000; pthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER; void *route(void *arg) { char *id (char *)arg; while (1) { pthread_mutex_lock(lock); if (ticket 0) // 1. 判断 { usleep(1000); // 模拟抢票花的时间 printf(%s sells ticket:%d\n, id, ticket); // 2. 抢到了票 ticket--; // 3. 票数-- pthread_mutex_unlock(lock); } else { pthread_mutex_unlock(lock); break; } } return nullptr; } int main(void) { pthread_t t1, t2, t3, t4; pthread_create(t1, NULL, route, (void *)thread 1); pthread_create(t2, NULL, route, (void *)thread 2); pthread_create(t3, NULL, route, (void *)thread 3); pthread_create(t4, NULL, route, (void *)thread 4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); }被保护起来的共享资源叫做临界资源3.理解为什么数据会不一致认识加锁的接口多线程---抢票为什么会减到负数--不是主要矛盾但是和它有关1.ticket--不是原子的C语言-汇编语言暂时 当前一条汇编就是原子的3条汇编切换数据不一致问题CPU在执行执行流的代码1.内存把ticket值载入到CPU的ebx寄存器中2.CPU内做--3.减完写回内存更改pc指针是一个CPU寄存器存储下一条要执行的指令的内存地址票数为什么减到了负数if (ticket 0) ----也是一种计算逻辑计算---CPU判断1.载入2.判断是主要矛盾多个线程同时进入串行式--从而减到负数一个全局资源没有加保护可能会有并发问题---线程安全问题多线程中制造更多的并发更多的切换---切换的时间点是什么时候1.时间片 2.阻塞是IO 3.sleep等...(切走)陷入内核选择新的从内核态返回用户态的时候进行检查引入锁:pthread_mutex_t 互斥锁/互斥量#include pthread.hint pthread_mutex_destroy(pthread_mutex_t *mutex);//释放int pthread_mutex_init(pthread_mutex_t *restricct mutexconst pthread_mutexattr_t *restrict attr);//初始化全局pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;全局锁不需要被释放程序运行结束会自动释放申请锁int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁int pthread_mutex_trylock(pthread_mutex_t *mutex);//申请锁的非阻塞版本int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁加锁解锁都是原子的加锁要竞争申请锁多线程都得先看到锁锁本身就是临界资源申请锁的过程必须是原子的申请成功继续向后运行访问临界区代码访问临界资源失败阻塞挂起申请执行流|锁提供的能力的本质执行临界区代码由并行转换为串行(在我执行期间不会被打扰也是一种变相的原子性的表现)加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码testMutex.cpp// 操作共享变量会有问题的售票系统代码 #include iostream #include string #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h int ticket 1000; class ThreadData { public: ThreadData(const std::string n, pthread_mutex_t lock) : name(n), lockp(lock) { } ~ThreadData() {} std::string name; pthread_mutex_t *lockp; }; //加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码 void *route(void *arg) { ThreadData *td static_castThreadData *(arg); while (1) { pthread_mutex_lock(td-lockp); //if这部分是临界区其他是非临界区保护临界区通过加速和解锁 if (ticket 0) { usleep(1000); printf(%s sells ticket:%d\n, td-name.c_str(), ticket); ticket--; pthread_mutex_unlock(td-lockp); } else { pthread_mutex_unlock(td-lockp); break; } } return nullptr; } int main(void) { pthread_mutex_t lock; pthread_mutex_init(lock,nullptr);//初始化锁 pthread_t t1, t2, t3, t4; ThreadData *td1 new ThreadData(thread 1,lock); pthread_create(t1, NULL, route, td1); ThreadData *td2 new ThreadData(thread 2,lock); pthread_create(t2, NULL, route, td2); ThreadData *td3 new ThreadData(thread 3,lock); pthread_create(t3, NULL, route, td3); ThreadData *td4 new ThreadData(thread 4,lock); pthread_create(t4, NULL, route, td4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); pthread_mutex_destroy(lock);//销毁锁 return 0; }MakefiletestMutex:testMutex.cpp g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f testMutexrootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.2# make g -o testMutex testMutex.cpp -stdc11 -lpthread rootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.2# ./testMutex thread 1 sells ticket:1000 thread 1 sells ticket:999 thread 1 sells ticket:998 thread 1 sells ticket:997 thread 1 sells ticket:996 ........ thread 3 sells ticket:4 thread 3 sells ticket:3 thread 3 sells ticket:2 thread 3 sells ticket:1全局的testMutex.cpp// 操作共享变量会有问题的售票系统代码 #include iostream #include string #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h int ticket 1000; pthread_mutex_t glock PTHREAD_MUTEX_INITIALIZER;//全局的初始化 class ThreadData { public: ThreadData(const std::string n, pthread_mutex_t lock) : name(n), lockp(lock) { } ~ThreadData() {} std::string name; pthread_mutex_t *lockp; }; //加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码 void *route(void *arg) { ThreadData *td static_castThreadData *(arg); while (1) { //pthread_mutex_lock(td-lockp); pthread_mutex_lock(glock); //if这部分是临界区其他是非临界区保护临界区通过加速和解锁 if (ticket 0) { usleep(1000); printf(%s sells ticket:%d\n, td-name.c_str(), ticket); ticket--; //pthread_mutex_unlock(td-lockp); pthread_mutex_unlock(glock); } else { //pthread_mutex_unlock(td-lockp); pthread_mutex_unlock(glock); break; } } return nullptr; } int main(void) { pthread_mutex_t lock; pthread_mutex_init(lock,nullptr);//初始化锁 pthread_t t1, t2, t3, t4; ThreadData *td1 new ThreadData(thread 1,lock); pthread_create(t1, NULL, route, td1); ThreadData *td2 new ThreadData(thread 2,lock); pthread_create(t2, NULL, route, td2); ThreadData *td3 new ThreadData(thread 3,lock); pthread_create(t3, NULL, route, td3); ThreadData *td4 new ThreadData(thread 4,lock); pthread_create(t4, NULL, route, td4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); pthread_mutex_destroy(lock);//销毁锁 return 0; }MakefiletestMutex:testMutex.cpp g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f testMutexrootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.3# make g -o testMutex testMutex.cpp -stdc11 -lpthread rootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.3# ./testMutex thread 1 sells ticket:1000 thread 1 sells ticket:999 thread 1 sells ticket:998 thread 1 sells ticket:997 thread 1 sells ticket:996 thread 1 sells ticket:995 ..... thread 1 sells ticket:5 thread 1 sells ticket:4 thread 1 sells ticket:3 thread 1 sells ticket:2 thread 1 sells ticket:1进程间互斥共享内存shm,(pthread_mutex_t*)shm我们有两个线程先创建一个共享内存。锁本质就是变量直接就用共享内存头部的位置让两个线程都能看到把头部位置强转成pthread_mutex_t*。就可以直接使用init初始化destroy做销毁。对临界资源进行保护本质其实就是用锁来对临界区代码进行保护问题1如果有一个线程不遵守你的规则---写bug---所有线程都必须遵守问题2加速之后在临界区内部允许线程切换吗切换了会怎么样--不怎么样所有线程必须等我跑完允许切换的---但是不担心因为我当前线程并没有释放锁我是持有锁被切换的即使我不在其他线程也得等我回来执行完代码释放锁其他线程才能展开锁的竞争进入临界区站在外边的人是如何看待你的自习过程呢要么不用要么用完对外边的人才有意义原子性你的自习对外边的人是原子的4.理解锁锁的原理1.硬件及实现关闭时钟中断2.软件级实现• 为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和 内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周 期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。锁本质上就是一个标志位理解锁的前提进程/线程切换CPU内的寄存器硬件只有一套但CPU寄存器内的数据可以有多份各自一份当前执行流的上下文|换句话说如果把一个变量的内容交换到CPU寄存器内部本质是把该变量的内容获取到当前执行流的上下文中|当前CPU寄存器的硬件上下文(其实就是各个寄存器的内容)属于进程/线程私有的|所以我们用swapexchange将内存中的变量交换到CPU的寄存器中本质是当前线程/进程,在获取锁因为是交换不是拷贝所以内部1只有一份所以谁申请到谁持有锁线程A/B进寄存器要把寄存器清零但是清的都是自己的上下文数据。彼此不会影响也不怕中断exchange无法被中断线程A被切走带走自己的%al:1谁交换成功谁就持有锁了Mutex就相当于这把锁谁持有这个1谁就持有锁所以释放锁/解锁的时候只要往Mutex写1就可以了C11也提供了std::mutex lock;#includemutextestMutex.cpp// 操作共享变量会有问题的售票系统代码 #include iostream #includemutex #include string #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h int ticket 1000; pthread_mutex_t glock PTHREAD_MUTEX_INITIALIZER;//全局的初始化 std::mutex cpp_lock; class ThreadData { public: ThreadData(const std::string n, pthread_mutex_t lock) : name(n), lockp(lock) { } ~ThreadData() {} std::string name; pthread_mutex_t *lockp; }; //加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码 void *route(void *arg) { ThreadData *td static_castThreadData *(arg); while (1) { cpp_lock.lock(); //pthread_mutex_lock(td-lockp); //pthread_mutex_lock(glock); //if这部分是临界区其他是非临界区保护临界区通过加速和解锁 if (ticket 0) { usleep(1000); printf(%s sells ticket:%d\n, td-name.c_str(), ticket); ticket--; //pthread_mutex_unlock(td-lockp); //pthread_mutex_unlock(glock); cpp_lock.unlock(); } else { //pthread_mutex_unlock(td-lockp); //pthread_mutex_unlock(glock); cpp_lock.unlock(); break; } } return nullptr; } int main(void) { pthread_mutex_t lock; pthread_mutex_init(lock,nullptr);//初始化锁 pthread_t t1, t2, t3, t4; ThreadData *td1 new ThreadData(thread 1,lock); pthread_create(t1, NULL, route, td1); ThreadData *td2 new ThreadData(thread 2,lock); pthread_create(t2, NULL, route, td2); ThreadData *td3 new ThreadData(thread 3,lock); pthread_create(t3, NULL, route, td3); ThreadData *td4 new ThreadData(thread 4,lock); pthread_create(t4, NULL, route, td4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); pthread_mutex_destroy(lock);//销毁锁 return 0; }MakefiletestMutex:testMutex.cpp g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f testMutexMutex.hpp#pragma once #include iostream #include pthread.h namespace MutexMudole { class Mutex { public: Mutex() { pthread_mutex_init(_mutex, nullptr); } void Lock() { int n pthread_mutex_lock(_mutex); (void)n; } void Unlock() { int n pthread_mutex_unlock(_mutex); (void)n; } ~Mutex() { pthread_mutex_destroy(_mutex); } private: pthread_mutex_t _mutex; }; }testMutex.cpp// 操作共享变量会有问题的售票系统代码 #include iostream #includemutex #include string #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h #includeMutex.hpp using namespace MutexMudole; int ticket 1000; //pthread_mutex_t glock PTHREAD_MUTEX_INITIALIZER;//全局的初始化 //std::mutex cpp_lock; class ThreadData { public: ThreadData(const std::string n, Mutex lock) : name(n), lockp(lock) { } ~ThreadData() {} std::string name; Mutex *lockp; }; //加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码 void *route(void *arg) { ThreadData *td static_castThreadData *(arg); while (1) { //cpp_lock.lock(); //pthread_mutex_lock(td-lockp); //pthread_mutex_lock(glock); //if这部分是临界区其他是非临界区保护临界区通过加速和解锁 td-lockp-Lock(); if (ticket 0) { usleep(1000); printf(%s sells ticket:%d\n, td-name.c_str(), ticket); ticket--; //pthread_mutex_unlock(td-lockp); //pthread_mutex_unlock(glock); //cpp_lock.unlock(); td-lockp-Unlock(); } else { //pthread_mutex_unlock(td-lockp); //pthread_mutex_unlock(glock); //cpp_lock.unlock(); td-lockp-Unlock(); break; } } return nullptr; } int main(void) { // pthread_mutex_t lock; // pthread_mutex_init(lock,nullptr);//初始化锁 Mutex lock; pthread_t t1, t2, t3, t4; ThreadData *td1 new ThreadData(thread 1,lock); pthread_create(t1, NULL, route, td1); ThreadData *td2 new ThreadData(thread 2,lock); pthread_create(t2, NULL, route, td2); ThreadData *td3 new ThreadData(thread 3,lock); pthread_create(t3, NULL, route, td3); ThreadData *td4 new ThreadData(thread 4,lock); pthread_create(t4, NULL, route, td4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); //pthread_mutex_destroy(lock);//销毁锁 return 0; }MakefiletestMutex:testMutex.cpp g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f testMutexrootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.4# make g -o testMutex testMutex.cpp -stdc11 -lpthread rootiZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-27.4# ./testMutex thread 1 sells ticket:1000 thread 1 sells ticket:999 thread 1 sells ticket:998 thread 1 sells ticket:997 ..... thread 2 sells ticket:3 thread 2 sells ticket:2 thread 2 sells ticket:1RAII风格的互斥锁Mutex.hpp#pragma once #include iostream #include pthread.h namespace MutexMudole { class Mutex { public: Mutex() { pthread_mutex_init(_mutex, nullptr); } void Lock() { int n pthread_mutex_lock(_mutex); (void)n; } void Unlock() { int n pthread_mutex_unlock(_mutex); (void)n; } ~Mutex() { pthread_mutex_destroy(_mutex); } private: pthread_mutex_t _mutex; }; class LockGuard { public: LockGuard(Mutex mutex):_mutex(mutex) { _mutex.Lock(); } ~LockGuard() { _mutex.Unlock(); } private: Mutex _mutex; }; }testMutex.cpp// 操作共享变量会有问题的售票系统代码 #include iostream #include mutex #include string #include stdio.h #include stdlib.h #include string.h #include unistd.h #include pthread.h #include Mutex.hpp using namespace MutexMudole; int ticket 1000; class ThreadData { public: ThreadData(const std::string n, Mutex lock) : name(n), lockp(lock) { } ~ThreadData() {} std::string name; Mutex *lockp; }; // 加速尽量加锁的范围粒度要比较细尽可能的不要包含太多的非临界区代码 void *route(void *arg) { ThreadData *td static_castThreadData *(arg); while (1) { {//临界区 LockGuard guard(*td-lockp); // 加锁完成RAII风格的互斥锁的实现 if (ticket 0) { usleep(1000); printf(%s sells ticket:%d\n, td-name.c_str(), ticket); ticket--; } else { break; } } } return nullptr; } int main(void) { Mutex lock; pthread_t t1, t2, t3, t4; ThreadData *td1 new ThreadData(thread 1, lock); pthread_create(t1, NULL, route, td1); ThreadData *td2 new ThreadData(thread 2, lock); pthread_create(t2, NULL, route, td2); ThreadData *td3 new ThreadData(thread 3, lock); pthread_create(t3, NULL, route, td3); ThreadData *td4 new ThreadData(thread 4, lock); pthread_create(t4, NULL, route, td4); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); return 0; }MakefiletestMutex:testMutex.cpp g -o $ $^ -stdc11 -lpthread .PHONY:clean clean: rm -f testMutex//代码块进代码块开空间出代码块释放空间。所以两个不冲突不会重定义{int a10;}int a20;感谢你的观看期待我们下次再见

相关新闻

深入解析EDMA3控制器:事件与中断寄存器机制及实战应用

深入解析EDMA3控制器:事件与中断寄存器机制及实战应用

1. EDMA3控制器事件与中断机制概述在嵌入式系统,尤其是德州仪器(TI)的C6000系列DSP平台上,EDMA3控制器是数据搬移任务的核心引擎。它的价值在于将CPU从繁重的、重复性的数据搬运工作中彻底解放出来,让CPU能够专注于算法…

2026/7/28 20:22:18 阅读更多 →
Vim常用操作(编程常用)

Vim常用操作(编程常用)

Vim常用操作(编程常用)Vim 是 Linux 系统上最强大的文本编辑器之一,掌握它能极大提升编辑效率。本文整理了 Vim 最核心的操作,适合日常开发和快速查阅。一、三种模式 Vim 共有三种模式:命令模式(Normal)、编辑模式&…

2026/7/27 5:31:27 阅读更多 →
AI编程工具横向对比:7大维度(响应延迟、上下文长度、私有部署支持、API调用成本、调试辅助能力、多语言覆盖度、企业合规认证)深度测评,选错工具=每月多花27小时

AI编程工具横向对比:7大维度(响应延迟、上下文长度、私有部署支持、API调用成本、调试辅助能力、多语言覆盖度、企业合规认证)深度测评,选错工具=每月多花27小时

更多请点击: https://kaifayun.com 第一章:AI编程工具横向对比:7大维度深度测评总览 AI编程辅助工具正迅速重塑开发者工作流,但工具选择高度依赖具体场景与技术栈。本章基于实测数据,从代码生成质量、上下文理解深度、…

2026/7/26 19:20:49 阅读更多 →

最新新闻

Golang的内存管理--学习笔记

Golang的内存管理--学习笔记

Go的内存管理内存的分级arena区SpansGo中的mspan内存管理组件mcachemcentralmheap内存的分配流程内存的分级 使用的是多级分配的办法,将内存分为多级,进行分级管理,从而降低锁的粒度。并且将可用的堆内存采用二级管理,其中每个线…

2026/7/28 20:25:11 阅读更多 →
N_m3u8DL-RE终极指南:跨平台流媒体下载完整解决方案

N_m3u8DL-RE终极指南:跨平台流媒体下载完整解决方案

N_m3u8DL-RE终极指南:跨平台流媒体下载完整解决方案 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE …

2026/7/28 20:25:10 阅读更多 →
终极指南:uBlock Origin如何成为浏览器隐私保护的最佳解决方案

终极指南:uBlock Origin如何成为浏览器隐私保护的最佳解决方案

终极指南:uBlock Origin如何成为浏览器隐私保护的最佳解决方案 【免费下载链接】uBlock uBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean. 项目地址: https://gitcode.com/GitHub_Trending/ub/uBlock uBlock Origin是一款高效…

2026/7/28 20:25:10 阅读更多 →
5分钟掌握AI视频剪辑:FunClip智能语音识别剪辑完整指南

5分钟掌握AI视频剪辑:FunClip智能语音识别剪辑完整指南

5分钟掌握AI视频剪辑:FunClip智能语音识别剪辑完整指南 【免费下载链接】FunClip FunASR-powered video transcription, subtitle generation, and LLM-assisted clipping tool with a local Gradio UI. 项目地址: https://gitcode.com/GitHub_Trending/fu/FunCli…

2026/7/28 20:25:10 阅读更多 →
碧蓝航线全自动脚本终极指南:告别手动操作,解放你的游戏时间

碧蓝航线全自动脚本终极指南:告别手动操作,解放你的游戏时间

碧蓝航线全自动脚本终极指南:告别手动操作,解放你的游戏时间 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScri…

2026/7/28 20:25:10 阅读更多 →
jqgrid 单击行启用行编辑,切换行保存原编辑行

jqgrid 单击行启用行编辑,切换行保存原编辑行

为了加速表格互动编辑,我们往往希望通过选中行就触发了行编辑,完成行编辑后,再选中另一个行做编辑,同时上一个编辑行被自动保存,直至完成需要的编辑内容。 页面效果可能如下: 1)设置需要编辑的…

2026/7/28 20:24:10 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻