Linux----线程
一、基础概念对比
特性 | 进程 (Process) | 线程 (Thread) |
---|---|---|
资源分配 | 资源分配的基本单位(独立地址空间) | 共享进程资源 |
调度单位 | 操作系统调度单位 | CPU调度的最小单位 |
创建开销 | 高(需复制父进程资源) | 低(共享进程资源) |
通信方式 | 管道、共享内存、消息队列等IPC | 共享全局变量(需同步机制) |
隔离性 | 内存隔离,安全性高 | 共享内存,需处理竞争条件 |
典型组成 | 代码段+数据段+堆栈段+PCB | 线程ID+寄存器组+栈+线程控制块TCB |
二、线程组成详解
1. 核心组件
struct thread_struct {pthread_t tid; // 线程ID (8字节)void* stack_base; // 栈基地址 (8字节)size_t stack_size; // 栈大小 (Linux默认8MB)void* (*start_routine)(void*); // 入口函数指针void* arg; // 入口函数参数// 寄存器组保存区 (约52个寄存器,约416字节)// 包括:PC、SP、通用寄存器、浮点寄存器等
};
2. 关键特征
- 线程ID:
pthread_t
类型,进程内唯一 - 独立栈空间:每个线程拥有独立调用栈
- 共享资源:全局变量、堆内存、文件描述符等
三、线程创建与管理
1. 创建函数原型
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);
参数详解表
参数 | 类型 | 作用说明 |
---|---|---|
thread | pthread_t* | 输出参数,存储新线程ID |
attr | pthread_attr_t* | 线程属性(NULL使用默认属性):<br>▪ 栈大小<br>▪ 调度策略<br>▪ 分离状态 |
start_routine | void* (*)(void*) | 线程入口函数(返回值为线程退出状态) |
arg | void* | 传递给入口函数的参数 |
返回值
- 成功返回
0
- 失败返回错误码(非
errno
值,需用strerror
转换)
2. 编译指令
gcc program.c -lpthread -o program # 必须链接pthread库
四、线程生命周期管理
1. 线程终止方式
(1) 显式调用退出函数
void* worker(void* arg) {int* heap_result = malloc(sizeof(int));*heap_result = 100;pthread_exit((void*)heap_result); // 正确方式:堆内存传递// static int static_result = 200; // 替代方案:静态变量// pthread_exit((void*)&static_result);
}
关键特性:
- 状态值通过
pthread_join()
获取 - 返回值必须使用堆内存或全局/静态变量
- 主线程调用时仅结束自身执行流
(2) 入口函数返回
void* worker(void* arg) {static int result = 200; // 必须使用静态存储期变量return (void*)&result; // 等效于pthread_exit()
}
禁止行为:
int local_var = 300;
return (void*)&local_var; // 错误!栈空间失效
(3) 被其他线程取消
// 取消请求端
pthread_cancel(target_tid);// 被取消线程端
void* worker(void* arg) {pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);while(1) {pthread_testcancel(); // 设置取消点/* 长时间操作 */}
}
取消类型对比:
类型 | 行为特征 | 设置函数 |
---|---|---|
PTHREAD_CANCEL_DEFERRED | 延迟到下一个取消点 | pthread_setcanceltype() |
PTHREAD_CANCEL_ASYNCHRONOUS | 立即终止(可能破坏数据) | pthread_setcanceltype() |
(4) 进程级终止
int main() {pthread_t tid;pthread_create(&tid, NULL, worker, NULL);// return 0; // 错误!触发exit()终止所有线程pthread_exit(NULL); // 正确:仅结束主线程
}
进程终止规则:
exit()
立即终止整个进程- 主线程
return
会隐式调用exit()
- 建议主线程始终使用
pthread_exit()
2. 状态回收机制
(1) 阻塞回收(Joinable模式)
void* status;
int ret = pthread_join(tid, &status);
if(ret == 0) {printf("退出码: %d\n", *(int*)status);free(status); // 必须释放堆内存
}
限制条件:
- 每个线程只能被join一次
- 已分离线程无法join
(2) 自动回收(Detached模式)
// 创建时设置分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, worker, NULL);// 运行时分离
pthread_detach(existing_tid);
特性:
- 线程终止后自动回收资源
- 无法获取返回值
- 适用于后台任务线程
五、线程资源管理
1. 清理函数机制
void cleanup(void* arg) {printf("清理资源: %p\n", arg);free(arg);
}void* worker(void* arg) {void* res = malloc(1024);pthread_cleanup_push(cleanup, res); // 注册清理函数// 可能被取消的代码段while(1) {pthread_testcancel();/* 临界操作 */}pthread_cleanup_pop(1); // 执行清理并出栈return NULL;
}
触发条件:
pthread_cleanup_pop(非零值)
- 线程通过
pthread_exit()
退出 - 被其他线程取消
编码规范:
- push/pop必须成对出现
- 建议在可能被取消的代码段前注册
- 栈式管理(后进先出)
2. 属性管理
(1) 完整属性设置流程
pthread_attr_t attr;
pthread_attr_init(&attr); // 初始化// 设置分离状态
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 设置栈大小(2MB示例)
size_t stack_size = 2 * 1024 * 1024;
void* stack_addr = malloc(stack_size);
pthread_attr_setstack(&attr, stack_addr, stack_size);pthread_create(&tid, &attr, worker, NULL);pthread_attr_destroy(&attr); // 销毁属性
(2) 常用属性API
函数 | 功能描述 |
---|---|
pthread_attr_setdetachstate | 设置分离/结合状态 |
pthread_attr_setstacksize | 设置线程栈大小 |
pthread_attr_setguardsize | 设置栈溢出保护区大小 |
pthread_attr_setschedpolicy | 设置调度策略(FIFO/RR等) |
六、线程同步机制
1. 互斥锁完整实现
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void* thread_func(void* arg) {pthread_mutex_lock(&mutex);/* 临界区操作 */pthread_mutex_unlock(&mutex);return NULL;
}// 动态初始化方式
pthread_mutex_init(&mutex, NULL);
/* ... */
pthread_mutex_destroy(&mutex);
2. 同步机制对比
机制 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
互斥锁 | 共享资源访问控制 | 简单高效 | 可能产生死锁 |
读写锁 | 读多写少场景 | 提高读并发性能 | 写线程可能饿死 |
条件变量 | 线程间状态通知 | 精确唤醒机制 | 需配合互斥锁使用 |
信号量 | 资源数量控制 | 跨进程可用 | 功能相对基础 |
3. 全局变量竞争解决方案
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
int global_counter = 0;void* counter_thread(void* arg) {for(int i=0; i<100000; ++i) {pthread_mutex_lock(&mutex);global_counter++; // 原子操作pthread_mutex_unlock(&mutex);}return NULL;
}
优化建议:
- 尽量减小临界区范围
- 避免嵌套锁
- 使用trylock避免死锁
七、高级主题与最佳实践
1. 返回值处理规范
(1) 简单状态码
// 传递整型值
pthread_exit((void*)(intptr_t)error_code);// 接收端
int code = (int)(intptr_t)status;
(2) 复杂数据结构
struct Result {int code;char message[256];
};void* worker(void* arg) {struct Result* res = malloc(sizeof(struct Result));/* 填充数据 */pthread_exit(res);
}// 接收端
struct Result* res = (struct Result*)status;
free(res);
2. 线程设计准则
-
资源管理三原则:
- 谁分配谁释放
- 退出前释放非共享资源
- 使用RAII模式管理资源
-
锁使用规范:
// 推荐加锁方式 pthread_mutex_lock(&mutex); do {/* 临界区操作 */ } while(0); pthread_mutex_unlock(&mutex);// 避免的写法 if(condition) pthread_mutex_unlock(&mutex); // 易漏解锁
-
错误处理模板:
int ret = pthread_create(&tid, NULL, worker, NULL); if(ret != 0) {fprintf(stderr, "线程创建失败: %s\n", strerror(ret));exit(EXIT_FAILURE); }
3. 调试技巧
-
死锁检测:
- 使用
pthread_mutex_trylock()
测试锁状态 - 记录加锁顺序
- 使用Valgrind的Helgrind工具
- 使用
-
性能分析:
# 使用perf分析锁竞争 perf record -g -- ./program perf report
八、完整生命周期图示
graph TDA[线程创建] --> B{执行阶段}B -->|正常完成| C[资源回收]B -->|被取消| D[清理处理]C --> E[线程终止]D --> EE --> F[系统回收TID]style B fill:#f9f,stroke:#333,stroke-width:2pxstyle C fill:#9f9,stroke:#333style D fill:#f99,stroke:#333subgraph 关键状态BCDend
九、常见问题解决方案
1. 僵尸线程问题
现象:线程终止但未回收,占用系统资源
解决方案:
// 方案1:及时join
void* retval;
pthread_join(tid, &retval);
free(retval);// 方案2:设置分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, worker, NULL);
2. 返回值内存泄漏
错误示例:
void* worker() {int result = 42;pthread_exit(&result); // 栈内存泄露!
}
正确实践:
void* worker() {int* result = malloc(sizeof(int)); // 堆内存*result = 42;pthread_exit(result);
}
3. 取消点设置不足
问题表现:取消请求长期不响应
优化方案:
void* worker(void* arg) {pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);while(1) {pthread_testcancel(); // 每循环添加取消点/* 长时间操作 */}return NULL;
}
十、扩展知识
1. 线程与进程对比
特性 | 进程 | 线程 |
---|---|---|
资源开销 | 高(独立地址空间) | 低(共享地址空间) |
通信方式 | 管道、共享内存、信号等 | 全局变量、互斥锁、条件变量 |
容错性 | 一个进程崩溃不影响其他 | 线程崩溃导致整个进程终止 |
上下文切换成本 | 高 | 低 |
2. 可重入函数设计
安全函数特征:
- 不使用静态变量
- 不调用非可重入函数
- 所有数据通过参数传递
示例对比:
// 不安全版本
char* strtok(char* str, const char* delim) {static char* buffer; // 静态变量/* ... */
}// 可重入版本
char* strtok_r(char* str, const char* delim, char** saveptr) {/* 使用传入的saveptr保存状态 */
}
十一、信号量
一、信号量概述
- 本质:计数器+等待队列,用于控制多线程/进程对共享资源的访问
- 分类:
- 无名信号量:内存中匿名对象,用于线程间同步
- 有名信号量:文件系统可见,用于进程间同步
二、核心API详解
1. 无名信号量操作
int sem_init(sem_t *sem, int pshared, unsigned int value);
pshared
:0表示线程间共享,非0表示进程间共享value
:信号量初始值(可用资源数)
2. 有名信号量操作
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
int sem_unlink(const char *name); // 删除系统残留信号量
3. 通用操作函数
// P操作(申请资源)
int sem_wait(sem_t *sem); // 阻塞等待
int sem_trywait(sem_t *sem); // 非阻塞尝试// V操作(释放资源)
int sem_post(sem_t *sem);// 销毁信号量
int sem_destroy(sem_t *sem); // 无名信号量
int sem_close(sem_t *sem); // 有名信号量
三、使用框架
// 无名信号量使用流程
sem_t sem;
sem_init(&sem, 0, 1); // 初始化
sem_wait(&sem); // P操作
// 临界区操作...
sem_post(&sem); // V操作
sem_destroy(&sem); // 销毁// 有名信号量使用流程
sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);
sem_wait(sem);
// 临界区操作...
sem_post(sem);
sem_close(sem);
sem_unlink("/mysem"); // 最后使用后删除
四、线程同步示例
#include <semaphore.h>
#include <pthread.h>sem_t sem;
int shared_data;void* thread_func(void* arg) {sem_wait(&sem);// 操作共享资源shared_data++;sem_post(&sem);return NULL;
}int main() {sem_init(&sem, 0, 1);pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);sem_wait(&sem);printf("Shared data: %d\n", shared_data);sem_post(&sem);pthread_join(tid, NULL);sem_destroy(&sem);return 0;
}
五、线程属性管理
// 设置分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 或在线程创建后设置
pthread_detach(thread_id);// 主动让出CPU
pthread_yield(); // 或使用 usleep()
六、死锁专题
必要条件(需同时满足):
- 互斥访问
- 请求并保持
- 不可剥夺
- 循环等待
避免策略:
- 资源有序分配法
- 银行家算法
- 设置超时机制(使用sem_timedwait)
- 避免嵌套锁申请
- 使用valgrind等工具检测
七、注意事项
- 信号量初始值决定行为:
- 二进制信号量:初始值为1
- 计数信号量:初始值>1
- 必须检查API返回值:
if(sem_post(&sem) == -1) {perror("sem_post failed"); }
- 销毁前确保:
- 没有线程阻塞在信号量上
- 不能销毁已销毁的信号量
- 有名信号量的命名:
- 必须以斜杠开头(如"/mysem")
- 长度限制(通常NAME_MAX-4)
八、补充说明
-
POSIX vs System V信号量:
- POSIX更轻量,接口更简洁
- System V支持更复杂的控制
-
信号量与互斥锁的区别:
特性 信号量 互斥锁 所有者 无 有(锁定线程) 可递增值 是 否 跨进程使用 支持(有名) 一般不支持
练习、创建两个线程,线程1 打印 hello,线程2 打印 world,预期效果在屏幕上严格打印hello world
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>
#include<semaphore.h>char buf[1024];
sem_t sem_w;
sem_t sem_r;void *do_something(void *arg)
{while(1){sem_wait(&sem_w);printf("hello ");sem_post(&sem_r);sleep(1);}return NULL;
}void *do_something1(void *arg)
{while(1){sem_wait(&sem_r);printf("world!\n");sem_post(&sem_w);sleep(1);}return NULL;
}int main(int argc, const char *argv[])
{pthread_t tid1, tid2;int ret, ret1;sem_init(&sem_w,0,1);sem_init(&sem_r,0,0);if ((ret = pthread_create(&tid1, NULL, do_something, NULL)) != 0){errno = ret;perror("pthread_create for do_something fail");return -1;}if ((ret1 = pthread_create(&tid2, NULL, do_something1, NULL)) != 0){errno = ret1;perror("pthread_create for do_something1 fail");return -1;}pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}
十二、练习
练习1:创建一个线程
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>void * do_something(void *arg)
{printf("do copy file---\n");return NULL;
}int main(int argc, const char *argv[])
{pthread_t tid;int ret;if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0){errno = ret;perror("pthread_create fail");return -1;}printf("-----main-------\n");sleep(1);return 0;return 0;
}
练习2:创建多个线程
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>void * do_one(void *arg)
{printf("pthread 1 pid = %d\n",getpid());return NULL;
}void * do_two(void *arg)
{printf("pthread 2 pid = %d\n",getpid());return NULL;
}void * do_three(void *arg)
{printf("pthread 3 pid = %d\n",getpid());return NULL;
}typedef void *(*thread_cb_t)(void*);int main(int argc, const char *argv[])
{printf("---main--- pid = %d\n",getpid());pthread_t tid[3];int ret;thread_cb_t func[3] = {do_one,do_two,do_three};int i = 0;for(i = 0;i < 3;i++){if((ret = pthread_create(&tid[i],NULL,func[i],NULL)) != 0){errno = ret;perror("pthread1_create fail");return -1;}}sleep(1);return 0;return 0;
}
练习3:线程的关闭
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>void * do_something(void *arg)
{static int ret = 100;printf("do copy file---\n");//pthread_exit("i am dead\n");pthread_exit(&ret);//return NULL;
}int main(int argc, const char *argv[])
{pthread_t tid;int ret;if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0){errno = ret;perror("pthread_create fail");return -1;}printf("-----main-------\n");int *retval;//char *retval;pthread_join(tid,(void **)&retval);//printf("*retval = %s\n",retval);printf("*retval = %d\n",*retval);sleep(1);return 0;return 0;
}
练习4:多线程拷贝文件(缺陷当文件过大,会导致偏移量出错)
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>typedef struct
{int fd_s;int fd_d;int size;int len;int id;
}msg_t;void * do_copy (void *arg)
{msg_t p = *(msg_t*)arg;lseek(p.fd_s,p.size*p.id,SEEK_SET);lseek(p.fd_d,p.size*p.id,SEEK_SET);// printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p.id,p.fd_s,p.fd_d,p.size,p.len);//调试代码char buf[p.len];int ret = read(p.fd_s,buf,p.len);write(p.fd_d,buf,ret);return NULL;
}//cp src dest
int main(int argc, const char *argv[])
{if (argc!=3){printf("Usage: %s <src> <dest>\n",argv[0]);return -1;}int fd_s = open(argv[1],O_RDONLY);int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666);if (fd_s < 0 || fd_d < 0){perror("open fail");return -1;}int n = 0;printf("Input threads num: ");scanf("%d",&n);int i = 0;int ret = 0;pthread_t tid[n];msg_t msg[n];struct stat st;if (stat(argv[1],&st) < 0){perror("stat fail");return -1;}int f_len = st.st_size;for (i = 0; i < n; ++i){msg[i].fd_s = fd_s;msg[i].fd_d = fd_d;msg[i].size = f_len / n;msg[i].id = i;#if 1if (i == n-1){ msg[i].len = f_len - (f_len/n)*(n-1);}else {msg[i].len = f_len/n;}
#endifret = pthread_create(&tid[i],NULL,do_copy,&msg[i]);if (ret != 0){errno = ret;perror("pthread_create fail");return -1;}}printf("----main-----\n");for (i = 0; i < n; ++i)pthread_join(tid[i],NULL);close(fd_s);close(fd_d);return 0;
}
相关文章:
Linux----线程
一、基础概念对比 特性进程 (Process)线程 (Thread)资源分配资源分配的基本单位(独立地址空间)共享进程资源调度单位操作系统调度单位CPU调度的最小单位创建开销高(需复制父进程资源)低(共享进程资源)通信…...
自注意力机制和CNN的区别
CNN:一种只能在固定感受野范围内进行关注的自注意力机制。CNN是自注意力的简化版本。自注意力:具有可学习感受野的CNN。自注意力是CNN的复杂形态,是更灵活的CNN,经过某些设计就可以变为CNN。 越灵活、越大的模型,需要…...
【qt链接mysql】
首先根据自己qtcreater 下载mysql安装包 将mysql安装目录下的如下目录中的xxx\MySQL\MySQL Server 5.7\lib\libmysql.dll 拷贝到QT目录C:\Qt\5.7\mingw53_32\bin 下(当前这个也是我电脑上的Qt路径,请找到你Qt对应的bin路径) 直接在文win11上…...
Parameter 与 Param 有什么区别
Parameter 与 Param Parameter 与 Param 意思相同,在大多数情况下可以互换使用,它们在用法和语境有一些细微的区别 1、Parameter Parameter 是一个完整的单词,是正式术语,广泛用于数学、统计学、计算机科学、工程等领域 在数学…...
【前端】【功能函数】treeMapEach,对每个节点进行自定义转换的实用函数
一、功能说明 这是一个递归遍历树形结构数据,并对每个节点进行自定义转换的实用函数。它会对原始树中的每个节点执行 conversion 函数,最终生成一个包含转换后结果的新树结构。 二、核心作用 树形结构遍历:深度优先递归遍历所有子节点数据…...
C#初级教程(5)——解锁 C# 变量的更多奥秘:从基础到进阶的深度指南
一、变量类型转换:隐式与显式的门道 (一)隐式转换:编译器的 “贴心小助手” 隐式转换是编译器自动进行的类型转换,无需开发者手动干预。这种转换通常发生在将取值范围小的数据类型赋值给取值范围大的数据类型时&#…...
初步学习java 动态代理
前言 在学习 动态代理知识之前,可以先了解 反射 反射的复习-CSDN博客 场景 我们知道,一些大明星开演出会,要收门票,准备场景啥的。但很显然 明星,他们主要还是 唱歌,跳舞,和粉丝互动。那么 其…...
QT 基础知识点
1.基础窗口类QMainWindow qDialog Qwidget 随项目一起创建的窗口基类有三个可选QMainWindow qDialog Qwidget 1.1 Qwidget 是所有窗口的基类,只要是他的子类,或子类的子类,都具有他的属性。 右键项目 Add New -> Qt qt设计师界面类&am…...
unity学习53:UI的子容器:面板panel
目录 1 UI的最底层容器:canvas 1.1 UI的最底层容器:canvas 1.2 UI的合理结构 2 UI的子容器:面板panel 2.1 创建panel 2.2 面板的本质: image ,就是一个透明的图片,1个空容器 3 面板的属性 4 面板的…...
Qt如何将数据传入labview,Qt又如何从labview中读取数据?
Qt如何将数据传入labview,Qt又如何从labview中读取数据? Qt如何将数据传入labviewQt如何从labview中读取数据 Qt如何将数据传入labview Qt如何从labview中读取数据...
JWT使用教程
目录 JWT (JSON Web Token)1. JWT简介(1) 什么是JWT(2) JWT有什么用(3) JWT认证方式 2. JWT的组成部分3. 签名的目的4. JWT与Token的区别5 JWT的优势6 JJWT签发与验证token(1) 引入依赖(2) 创建 Token(3) 解析Token(4) 设置过期时间(5) 自定义claims 7. JWT自定义工具类 JWT (J…...
数据结构——静态顺序表,动态顺序表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使 ⽤的 数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在…...
前端Sass面试题及参考答案
目录 什么是 Sass? Sass 和 CSS 的主要区别是什么? Sass 中如何处理列表? Sass 中如何处理映射(map)? Sass 中如何使用函数? Sass 中如何使用内置函数? Sass 中如何设置默认值? Sass 中的 @function 和 @mixin 有什么区别? Sass 中如何实现模块化? Sass 中…...
ubuntu20.04音频aplay调试
1、使用指定声卡,aplay 播放命令 aplay -D plughw:1,0 test2.wav2、 录音 arecord -Dhw:1,0 -d 10 -f cd -r 44100 -c 2 -t wav test.wav3、各个参数含义 -D 指定声卡编号 plughw:0,0 //0,0代表card0,device0,可以通过arecord -l获取 -f 录音格式 S16_LE…...
比特信噪比与信噪比SNR的换算公式
在无线通信系统中,比特信噪比与信噪比(SNR,通常指符号信噪比Es/N0)的换算: 核心公式 E b N 0 SNR R ⋅ log 2 M \boxed{ \frac{E_b}{N_0} \frac{\text{SNR}}{R \cdot \log_2 M} } N0EbR⋅log2MSNR 或…...
RTSP场景下RTP协议详解及音视频打包全流程
RTSP场景下RTP协议详解及音视频打包全流程 一、RTSP与RTP的关系 RTSP:负责媒体会话控制(DESCRIBE、SETUP、PLAY、PAUSE),通过SDP协商传输参数(端口、编码格式、封装模式)。RTP:实际传输音视频数…...
java练习(39)
ps:题目来自力扣 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以…...
2.24DFS和BFS刷题
洛谷P2895:用BFS走出危险区域,危险区域存在时间,我们用ma记录最快变成危险区域的时间, 然后每次枚举时间1然后跟ma数组比较看能不能走,然后时间复杂度为O(305^2)。 #include<iostream> #include<cstring>…...
基于YOLO11深度学习的运动鞋品牌检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
赛前启航 | 三场重磅直播集结,予力微软 AI 开发者挑战赛!
随着微软 AI 开发者挑战赛的火热进行,赛前指导直播已成为众多参赛者获取技术干货、灵感碰撞和实战技巧的绝佳平台。继前两期的精彩呈现,第三、四、五期直播即将接连登场,为开发者们带来更加深入的 AI 技术剖析和项目实战指引。无论你是想进一…...
MySQL数据库连接池泄露导致MySQL Server超时关闭连接
前言 最近做项目,发现老项目出现xxx,这个错误其实很简单,出现在MySQL数据库Server端对长时间没有使用的client连接执行清楚处理,因为是druid数据库,且在github也出现这样的issue:The last packet successf…...
Deepseek和Grok 3对比:写一段冒泡排序
1、这是访问Grok 3得到的结果 2、grok3输出的完整代码: def bubble_sort(arr):n len(arr) # 获取数组长度# 外层循环控制排序轮数for i in range(n):# 内层循环比较相邻元素,j的范围逐渐减少for j in range(0, n - i - 1):# 如果当前元素大于下一个元…...
EX_25/2/22
找到第一天mystring练习,实现以下功能 mystring str "hello" mystring ptr "world" str str ptr; str ptr str[0] H #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #in…...
el-select滚动获取下拉数据;el-select滚动加载
el-select下拉获取数据 1.解决问题2.封装MyScrollSelect组件3.使用MyScrollSelect组件 1.解决问题 场景:下拉数据量过大,后端提供一个分页查询接口;需要每次滚动加载下一页的下拉数据 且单选的状态,需要支持回显,通过n…...
Spring Boot面试题
Spring Boot面试题 基础概念 Q1: Spring Boot的核心特性有哪些? public class SpringBootBasicDemo {// 1. 自动配置SpringBootApplicationpublic class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class…...
STM32-智能小车项目
项目框图 ST-link接线 实物图: 正面: 反面: 相关内容 使用L9110S电机模块 电机驱动模块L9110S详解 | 良许嵌入式 一、让小车动起来 新建文件夹智能小车项目 在里面复制19-串口打印功能 重命名为01-让小车动起来 新建文件夹motor&…...
SAP-ABAP:ABAP第一代增强详解
在SAP ABAP开发中,第一代增强(First-Generation Enhancement) 是早期用于扩展标准程序功能的传统技术,主要通过预定义的增强点(Enhancement Points)实现。以下是详细解析: 一、第一代增强的核心…...
20分钟 Bash 上手指南
文章目录 bash 概念与学习目的第一个 bash 脚本bash 语法变量的使用位置参数管道符号(过滤条件)重定向符号条件测试命令条件语句case 条件分支Arrayfor 循环函数exit 关键字 bash 脚本记录历史命令查询文件分发内容 bash 概念与学习目的 bash࿰…...
地铁站内导航系统:基于蓝牙Beacon与AR技术的动态路径规划技术深度剖析
本文旨在分享一套地铁站内导航系统技术方案,通过蓝牙Beacon技术与AI算法的结合,解决传统导航定位不准确、路径规划不合理等问题,提升乘客出行体验,同时为地铁运营商提供数据支持与增值服务。 如需获取校地铁站内智能导航系统方案文…...
WordPress R+L Carrier Edition sql注入漏洞复现(CVE-2024-13481)(附脚本)
免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…...
滴水逆向_引用_友元函数_运算符重载
作业: 运算符号重载实现。 struct Person { public:int x;int y; public:Person(){this->x 10;this->y 20;}Person(int x, int y){this->x x;this->y y;}//申明友元函数void Printf(const Person& p){printf("%d %d",p.x,p.y);}/…...
git中,如何查看具体单个文件的log
在 Git 中,可以使用多种方式查看单个文件的提交日志(Log),以下详细介绍不同场景下的查看方法: 目录 一、基本命令查看文件的完整提交日志 二、查看文件提交日志并显示差异内容 三、限制显示的提交日志数量 四、按…...
如何生成traceid以及可视化展示
根据你的需求,以下是一些可以生成唯一 traceId 并用于分布式链路追踪的工具和项目,这些项目支持生成唯一的 traceId,并将其用于日志记录和分布式追踪: 1. OpenTelemetry OpenTelemetry 是一个开源的观测框架,支持生成…...
2024 ICPC香港站 L.Flipping Paths的一种解法
太变态了,场上被硬控了两个小时,最后20分钟思路熬出来了但是没写对~,糖完了。怎么说呢,香港站这样的轻量级赛站,这次强队也很少,导致很多题目的难度升级了,这道L题是一道银牌题,不少…...
Uniapp 开发中遇到的坑与注意事项:全面指南
文章目录 1. 引言Uniapp 简介开发中的常见问题本文的目标与结构 2. 环境配置与项目初始化环境配置问题解决方案 项目初始化注意事项解决方案 常见错误与解决方案 3. 页面与组件开发页面生命周期注意事项示例代码 组件通信与复用注意事项示例代码 样式与布局问题注意事项示例代码…...
Python - 代码片段分享 - Excel 数据实时写入方法
文章目录 前言注意事项工具 pandas1. 简介2. 安装方式3. 简单介绍几个api 实战片段 - 实时写入Excel文件结束语 要么出众,要么出局 前言 我们在爬虫采集过程中,总是将数据解析抓取后统一写入Excel表格文件,如果在解析数据出现问题容易出现数据…...
一文详解U盘启动UEFI/Legacy方式以及GPT/MBR关系
对于装系统的老手而说一直想研究一下装系统的原理,以及面对一些问题时的解决思路,故对以前的方法进行原理上的解释,主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导,我们可以看一下微pe制作的启动盘&#…...
Java函数式接口的巧妙应用
引言 函数式接口(Functional Interface)是Java 8引入的一个重要概念,它是Lambda表达式和方法引用的基础。通过函数式接口,Java实现了对函数式编程的支持,让代码更加简洁、灵活。本文将带你深入理解函数式接口…...
爱普生SG-8101CE可编程晶振赋能智能手机的精准心脏
在智能手机高速迭代的今天,高性能、低功耗与小型化已成为核心诉求。智能手机作为人们生活中不可或缺的工具,需要在各种复杂场景下稳定运行。爱普生SG-8101CE可编程晶振凭借其卓越性能,成为智能手机中不可或缺的精密时钟源,为通信、…...
条件渲染
当if条件为true则会被显示出来,若为false则不会显示出来。 在App.vue中需要引用一下。 if else一样的if为真则显示if的内容,若不是则显示else下的内容。 多条件判断。 if在为false时,根本不会渲染,而show则会,只不过d…...
sklearn中的决策树-分类树:剪枝参数
剪枝参数 在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树 往往会过拟合。为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策…...
算法随笔_59: 子数组最小乘积的最大值
上一篇:算法随笔_58: 队列中可以看到的人数-CSDN博客 题目描述如下: 一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的 和 。 比方说,数组 [3,2,5] (最小值是 2)的最小乘积为 2 * (325) 2 * 10 20 。 给你一个正整数数组 nums …...
线性模型 - 支持向量机延伸
为了更好的理解支持向量机模型,本文我们延伸学习和理解一下和支持向量机相关的一些概念,这些概念都是偏理论和数学的知识,比较抽象和复杂,而且需要一定的高等数学知识。大家可以先明白其所包含的意义,然后逐步深入理解…...
力扣3464. 正方形上的点之间的最大距离
力扣3464. 正方形上的点之间的最大距离 题目 题目解析及思路 题目要求在points集合中找出k个点,k个点之间的最小的曼哈顿距离的最大值 最大最小值的题一般直接想到二分 将正方形往右展开成一条线,此时曼哈顿距离为两点直线距离**(仅起点右边的点)** …...
AI数字人源码搭建部署指南
为实现AI数字人的智能交互功能,需开发包含语音识别、自然语言处理、机器学习等技术的AI算法和模型。利用TensorFlow、PyTorch等深度学习框架完成模型训练。具体步骤包括以下四个方面: 需求分析:通过市场调研、用户访谈、专家咨询等方式&…...
智能拖把控制板开发
智能拖把控制板开发全流程解析 一、硬件架构与H桥驱动设计 工程师小明选用四颗AO3400A低导通电阻MOS管构建H桥驱动拓扑,实测全桥导通电阻仅15mΩ,较传统方案降低40%损耗。通过优化PCB布局将驱动环路电感控制在15nH以内,配合10kHz互补PWM信号…...
【Swift 算法实战】利用 KMP 算法高效求解最短回文串
网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...
【数字化转型+AI:现代企业腾飞的一对翅膀】
——解析数字时代的核心竞争力 在全球化竞争与技术迭代加速的今天,传统企业若想突破增长瓶颈,必须抓住两大核心驱动力:数字化转型与人工智能(AI)。这两大技术如同企业腾飞的双翼,共同构建敏捷性、创…...
Zabbix——踩坑HttpRequest,header添加无效
背景 在试图尝试通过Zabbix接入DeepSeek API的时候,由于使用了HTTP的方式,所以需要使用Zabbix 自带的HttpRequest库进行请求,产生了下面的问题 问题 curl curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completio…...
MTK Android12 预装apk可卸载
文章目录 需求解决方法1、device/mediatek/mt6761/device.mk2、/vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_removable_vendor_list.txt3、路径:4、Android.mk 需求 近期,客户需要预装一个apk,同时该apk要可卸载。解…...