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

Linux的多线程(线程的创建,退出,取消请求,取消处理例程,线程属性的设置)

进程:是系统分配资源的最小单位,系统会为每一个进程分配一块独立的虚拟内存空间

线程:是系统调度的最小单位,系统不会为线程分配新的内存空间,但是线程也参与系统调度

cpu把时间片分给每一个进程,进程中的时间片再切分分给每一个线程,所以线程也会得到时间片,所以线程使系统调度的最小单位

线程的创建

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                                  void *(*start_routine) (void *), void *arg);

pthread_t *thread      //线程的tid
const pthread_attr_t *attr    //线程的属性
void *(*start_routine) (void *)    //线程的任务函数
void *arg    //传递给任务函数的参数

编译时的时候记得添加  -lpthread

 void *(*start_routine) (void *) 函数指针,指向需要执行的任务函数 
 任务函数返回值必须为  void *, 参数必须为 void *,回调函数

#include <stdio.h>
#include <pthread.h>// 任务线程
void *task1(void *arg)
{int j = 0;while (1){printf("j=%d\n", j++);}
}// 任务线程
void *task2(void *arg)
{int i = 0;while (1){printf("i=%d\n", i++);}
}// 任务线程
void *task3(void *arg)
{int k = 0;while (1){printf("k=%d\n", k++);}
}int main()
{pthread_t tid1;pthread_create(&tid1, NULL, task1, NULL);pthread_t tid2;pthread_create(&tid2, NULL, task2, NULL);pthread_t tid3;pthread_create(&tid3, NULL, task3, NULL);getchar();  //调用一个阻塞函数不让进程结束
}

 线程传递参数

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>struct arg
{int a;double b;char str[1024];
} ;// 线程任务函数
void *task(void *arg)
{struct arg *p = arg;printf("整数1 %d\n", p->a);printf("浮点1 %f\n", p->b);printf("字符1 %s\n", p->str);}int main()
{struct arg arg = {15678, 156.78, "15678"};// 创建一个线程pthread_t tid;pthread_create(&tid, NULL, task, &arg);  //&arg是线程函数中的参数sleep(1);printf("整数2 %d\n", arg.a);printf("浮点2 %f\n", arg.b);printf("字符2 %s\n", arg.str);getchar(); //阻塞主进程
}

线程退出

退出当前线程

#include <pthread.h>
void pthread_exit(void *retval);

参数retval是线程的返回值,对应线程执行函数的返回值。若线程没有数据可返回则可写成NULL。

pthread_exit()用法可参照exit()

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *task(void *arg) // 子线程
{int i = 0;while (1){printf("i=%d\n", i++);sleep(1);if (i == 10){printf("退出子线程\n");pthread_exit(NULL); // 退出子线程}}
}int main() // main 主线程
{// 创建线程线程pthread_t tid;pthread_create(&tid, NULL, task, NULL);// 默认情况,主函数结束,那么进程也会结束,所有线程都会死亡!printf("退出主线程\n");pthread_exit(NULL); // 退出  main主函数线程。只是结束线程,进程并未结束// 所以就算主线程结束了子线程也还是会继续运行
}

回收一个线程资源,主线程阻塞等待子线程结束,然后回收子线程资源

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval)

thread: 需要回收资源的线程tid
retval:线程的退出参数,参数同上填NULL即可

通过阻塞等待线程执行完回收资源 

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 任务线程
void *task(void *arg)
{int i = 0; // 局部变量,属于线程的栈空间while (1){printf("线程正在执行任务 &i=%p %d\n", &i, i++);if (i == 10){pthread_exit(&i); // 退出线程}sleep(1);}
}int main()
{// 1.创建一个线程pthread_t tid;pthread_create(&tid, NULL, task, NULL);printf("等待线程结束回收资源\n");void *p = NULL;pthread_join(tid, &p); // 一直阻塞等待线程退出 ! 回收线程的栈空间printf("线程结束 退出参数:%p\n", p);printf("退出参数 %d\n", *((int *)p)); // 访问已经回收后的内存资源,非法访问!!exit(0);}

如果没有pthread_join那么线程根本不会执行完,直接在主线程中就exit(0)退出进程了


线程取消

发送一个取消命令给线程

#include <pthread.h>
int pthread_cancel(pthread_t thread);

thread:需要取消的线程 tid
成功返回 0,失败将返回错误码

pthread_self()   返回主线程的线程号tid

线程取消请求

        当线程收到一个取消请求时,他将会如何表现取决于两个东西:一是当前的取消状态,二是当前的取消类型。

        线程的取消状态很简单--分别是PTHREADCANCEL ENABLE和 PTHREAD CANCEL DISABLE,前者是缺省的,代表线程可以接受取消请求,后者代表关闭取消请求,不对其响应。

        而在线程接受取消请求的情况下,如何停下来又取决于两种不同的响应取消请求的策略一延时响应和立即响应,当采取延时策略时,线程并不会立即退出,而是要遇到所谓的“取消点”之后,才退出。而“取消点”,指的是一系列指定的函数。

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);//开启或关闭取消请求

state : 新的取消状态              PTHREAD_CANCEL_ENABLE 开启
                                               PTHREAD_CANCEL_DISABLE 关闭
oldstate:原来的状态

int pthread_setcanceltype(int type, int *oldtype); //设置取消类型

type:取消类型                                    PTHREAD_CANCEL_DEFERRED 延时取消 (默认类型)
                                                          PTHREAD_CANCEL_ASYNCHRONOUS 立即取消

返回值:成功 0

               失败errno
 

设置线程取消请求demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 任务线程
void *task(void *arg)
{// 关闭取消请求pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);int i = 0;while (1){printf("线程正在执行任务 %d\n", i++);if (i == 10){printf("执行完毕\n");break;}sleep(1);}// 开启取消请求pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);while (1){printf("线程正在运行\n");sleep(1);}
}int main()
{// 1.创建一个线程pthread_t tid;pthread_create(&tid, NULL, task, NULL);while (1){printf("输入任意键取消线程\n");getchar();pthread_cancel(tid);}
}

 在关闭线程取消请求之后,线程无法被取消,只有在重新打开线程取消请求之后的第二个循环中才能通过pthraed_cancel取消线程

线程取消处理函数

        由于线程任何时刻都有可能持有诸如互斥锁、信号量等资源,一旦被取消很有可能导致别的线程出现死锁,因此如果一条线程的确可能被取消,那么在被取消之前必须使用以下API来为将来可能出现的取消请求注册“处理例程”,让这些例程自动释放持有的资源。

注册一个取消处理函数,当线程被取消时,会去执行该函数

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);

routine: 函数指针,指向线程被取消后需要执行的函数
arg:传递给取消处理函数使用的参数

void pthread_cleanup_pop(int execute);

execute: 0 不执行,线程正常结束不执行取消处理函数 
               1 执行,线程正确结束执行取消处理函数(只要线程被取消,都会执行取消处理函数)

pthread_cleanup_push必须与 pthread_cleanup_pop 一起使用,且在同一个作用域中

注册一个线程取消处理函数demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
FILE *fp = NULL;void clear_task(void *arg)
{printf("线程被取消,执行取消处理函数\n");fclose(fp); // 关闭文件,刷新缓存
}void *task(void *arg)
{int i = 0;// 打开文件fp = fopen("test.txt", "w+");// 注册一个取消处理函数pthread_cleanup_push(clear_task, NULL);while (1) // 正在运行的时候被别人取消了{fputc('Q', fp); // 写入文件printf("线程正在写入数据到文件中\n");sleep(1);i++;if (i == 10){break;}}//pthread_cleanup_pop(0); // 正常结束的时候,不执行pthread_cleanup_pop(1); //正常结束的时候, 执行printf("线程正常结束\n");fclose(fp); // 关闭文件,刷新缓存
}int main()
{// 1.创建一个线程pthread_t tid;pthread_create(&tid, NULL, task, NULL);while (1){printf("按任意键取消线程\n");getchar();pthread_cancel(tid);}return 0;
}

 在这段代码中当pthread_cleanup_pop中的值为1时,线程正常结束时也会执行clean_task中的函数,如果excute的值为0的话,那么只有在线程执行的过程中被打断会执行clean_task函数。

线程属性的设置

线程相关的api

 

 

查看系统的资源

loading@DESKTOP-R0NLPTR:~$ ulimit -a
real-time non-blocking time  (microseconds, -R) unlimited
core file size              (blocks, -c) 0              #core文件的最大值为100 blocks。
data seg size               (kbytes, -d) unlimited    #进程的数据段可以任意大。
scheduling priority                 (-e) 0        #调度优先级
file size                   (blocks, -f) unlimited     #文件可以任意大。
pending signals                     (-i) 25348       #最多有25348个待处理的信号。
max locked memory           (kbytes, -l) 64          #一个任务锁住的物理内存的最大值为32KB。
max memory size             (kbytes, -m) unlimited    #一个任务的常驻物理内存的最大值。
open files                          (-n) 1024        #一个任务最多可以同时打开1024的文件。
pipe size                (512 bytes, -p) 8        #管道的最大空间为4096字节。
POSIX message queues         (bytes, -q) 819200     #POSIX的消息队列的最大值为819200字节。
real-time priority                  (-r) 0
stack size                  (kbytes, -s) 8192        #进程的栈的最大值为10240字节。
cpu time                   (seconds, -t) unlimited    #进程使用的CPU时间。
max user processes                  (-u) 25348    #当前用户同时打开的进程(包括线程)的最大个数为25348。
virtual memory              (kbytes, -v) unlimited    #没有限制进程的最大地址空间。
file locks                          (-x) unlimited    #所能锁住的文件的最大个数没有限制。

设置线程属性的流程

1、初始化线程属性

2、设置线程属性

3、根据设置的线程属性创建线程

4、销毁线程和属性

线程属性也是线程创建的第二个参数

线程属性的创建和销毁

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

       

线程属性的设置

#include <pthread.h>

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); //设置栈大小 
int pthread_attr_getstacksize(const pthread_attr_t *attr,

                                                   size_t *stacksize);//获取栈大小

线程栈空间的设置
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *task(void *arg)
{int i = 0;while (1){printf("线程正在运行 %d\n", i++);sleep(1);if (i == 10){// 退出线程pthread_exit(NULL);}}
}int main()
{// 初始化一个线程属性pthread_attr_t attr;pthread_attr_init(&attr);int n;printf("请输入需要设置的线程大小\n");scanf("%d", &n);// 设置线程属性//分配的栈空间必须比系统分配最小的栈空间要大,//本机最小栈空间为16384,下文给出查看最小分配栈空间办法int size = 16384 + n;// int size = 1024;pthread_attr_setstacksize(&attr, size);// 根据当前的栈大小创建线程pthread_t tid;pthread_create(&tid, &attr, task, NULL);// 获取当前线程的栈大小size_t stacksize = 0;pthread_attr_getstacksize(&attr, &stacksize); // 获取栈大小printf("当前线程的栈大小 %ld\n", stacksize);// 回收线程资源pthread_join(tid, NULL);// 销毁属性pthread_attr_destroy(&attr);
}

获取本机当前需要分配的最小栈空间

stack_min.c

#include <stdio.h>
#include <limits.h>int main()
{printf("%d\n", PTHREAD_STACK_MIN);
}

可以看到本机的栈空间为16384

loading@DESKTOP-R0NLPTR:~/ gcc stack_min.c && ./a.out
16384
 线程分离属性设置

        一条线程如果是可接合的,意味着这条线程在退出时不会自动释放自身资源,而会成为僵尸线程,同时意味着该线程的退出值可以被其他线程获取。

        因此,如果不需要某条线程的退出值的话,那么最好将线程设置为分离状态,以保证该线程不会成为僵尸线程。

#include <pthread.h> 

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 

detachstate: 线程分离属性           PTHREAD_CREATE_DETACHED 分离状态,自动回收资源 
                                                        PTHREAD_CREATE_JOINABLE   结合状态,手动回收资源 

注意:当一个线程设置为分离属性后,pthread_join 函数失效了! 因为该线程已经无需手动回收资源!没有返回值的函数是宏定义函数!

设置分离属性自动回收资源demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *task(void *arg)
{int i = 0;while (1){printf("线程正在运行 %d\n", i++);sleep(1);if (i == 10){// 退出线程pthread_exit(NULL);}}
}int main()
{// 初始化一个线程属性pthread_attr_t attr;pthread_attr_init(&attr);// 设置线程分离属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 根据当前的栈大小创建线程pthread_t tid;pthread_create(&tid, &attr, task, NULL);printf("等待线程结束\n");// 回收线程资源pthread_join(tid, NULL); // 失效,因为线程自己回收资源!printf("线程资源回收\n");// 销毁属性pthread_attr_destroy(&attr);getchar();}

主线程与子线程自动分离,子线程结束后,资源自动回收

#include <pthread.h>

int pthread_detach(pthread_t thread)

pthread_join()函数的替代函数,可回收创建时detachstate属性设置为PTHREAD_CREATE_JOINABLE的线程的存储空间。该函数不会阻塞父线程。pthread_join()函数用于只是应用程序在线程tid终止时回收其存储空间。如果tid尚未终止,pthread_detach()不会终止该线程

快速回收资源demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *task(void *arg)
{int i = 0;while (1){printf("线程正在运行 %d\n", i++);sleep(1);if (i == 10){// 退出线程pthread_exit(NULL);}}
}int main()
{// 根据当前的栈大小创建线程pthread_t tid;pthread_create(&tid, NULL, task, NULL);// 快速设置分离属性pthread_detach(tid);printf("等待线程结束\n");// 回收线程资源pthread_join(tid, NULL); // 失效,因为线程自己回收资源!printf("线程资源回收\n");getchar();
}

 

相关文章:

Linux的多线程(线程的创建,退出,取消请求,取消处理例程,线程属性的设置)

进程:是系统分配资源的最小单位,系统会为每一个进程分配一块独立的虚拟内存空间 线程:是系统调度的最小单位,系统不会为线程分配新的内存空间,但是线程也参与系统调度 cpu把时间片分给每一个进程&#xff0c;进程中的时间片再切分分给每一个线程&#xff0c;所以线程也会得到…...

git 本地代码关联远程仓库并推送

初始化代码仓库 如果你的本地项目还没有使用Git管理&#xff0c;首先需要在项目根目录下初始化一个Git仓库 git init添加远程仓库地址 使用 git remote add 命令添加远程仓库 git remote add origin https://github.com/username/repository.git获取远程分支信息 使用 git…...

推荐一个可以把PDF样本册转换为翻页电子书的网站

​随着互联网的普及&#xff0c;越来越多的企业和个人开始意识到线上展览的重要性。如何将实体样本册转化为线上版本&#xff0c;让更多人了解和欣赏自己的产品与服务&#xff1f; 一、网站简介 这款PDF样本册免费上传网站名为“FLBOOK”&#xff0c;致力于为广大用户提供便捷…...

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…...

Rust SQLite 跨平台使用

引言 Rust因其内存安全性和高性能受到越来越多开发者的青睐。在许多项目中&#xff0c;SQLite作为一种轻量级的嵌入式数据库&#xff0c;与Rust的结合为跨平台应用程序提供了强大的支持。本文将详细探讨Rust如何实现跨平台功能&#xff0c;如何在不同平台上使用Rust库&#xf…...

docker运行arm64架构的镜像、不同平台镜像构建

背景 Docker 允许开发者将应用及其依赖打包成一个轻量级、可移植的容器&#xff0c;实现“一次构建&#xff0c;到处运行”的目标。然而&#xff0c;不同的操作系统和硬件架构对容器镜像有不同的要求。例如&#xff0c;Linux 和 Windows 系统有不同的文件系统和系统调用&#…...

vue基于Spring Boot框架的高校实验室预约管理系统

目录 毕设制作流程功能和技术介绍系统实现截图开发核心技术介绍&#xff1a;使用说明开发步骤编译运行代码执行流程核心代码部分展示可行性分析软件测试详细视频演示源码获取 毕设制作流程 &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&am…...

Linux中find命令详解

记录linux中find命令的详细用法。 文章目录 find命令简介基本语法常用选项-name-iname-type-size-mtime,-atime,-ctime-perm-user-group-delete-exec-printand or find --help find命令简介 find 是一个搜索目录树以查找一个文件或一组文件的程序。它遍历目录树并报告与用户规…...

无水印短视频素材下载网站有哪些?十个高清无水印视频素材网站分享

你知道怎么下载无水印视频素材吗&#xff1f;今天小编就给大家推荐十个高清无水印视频素材下载的网站&#xff0c;如果你也是苦于下载高清无水印的短视频素材&#xff0c;赶紧来看看吧&#xff5e; 1. 稻虎网 首推的是稻虎网。这个网站简直就是短视频创作者的宝库。无论你需要…...

SpringBoot+Activiti7工作流入门实例

目录 文章目录 目录准备Activiti建模工具1、BPMN-js在线设计器1.1 安装1.2 使用说明1.3运行截图2、IDEA安装Activiti Designer插件2.1安装插件2.2 设置编码格式防止中文乱码2.3 截图简单工作流入门实例1. 新建Spring Boot工程2. 引入Activiti相关依赖添加版本属性指定仓库添加依…...

Azure OpenAI检索增强微调:使用 GPT-4o 对 GPT-4o mini 进行微调,以适应特定领域的应用

定制是关键&#xff01; 生成式人工智能对企业最有影响力的应用之一是创建自然语言界面&#xff0c;这些界面经过定制&#xff0c;可以使用特定领域和用例数据来提供更好、更准确的响应。这意味着回答有关特定领域的问题&#xff0c;例如银行、法律和医疗领域。 我们经常谈…...

ISP Pipeline

系列文章目录 文章目录 系列文章目录前言一、RAW域二、RGB域三、YUV域总结 前言 一、RAW域 黑电平校正&#xff08;BLC&#xff09;数字增益调整&#xff08;DGain&#xff09;自动白平衡&#xff08;AWB&#xff09;局部色调映射&#xff08;LTM&#xff09;坏点修复&#xf…...

< IDE编程环境配置>

IDE编程环境配置 LIB&#xff0c;DLL区别 我们在写项目时会链接&#xff08;调用&#xff09;第3方库&#xff0c;或者比如在vs的解决方案solution创建项目project时&#xff0c;不仅可以开发可执行程序exe&#xff08;可单独运行&#xff09;&#xff08;windows/控制台 应用…...

Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; func findDisappearedNumbers(nums []int) (ans []int) {n : len(nums)for _, v : range nums {v (v - 1) % nnums[v] n}for i, v : range nums {if v < n {ans append(ans, i1)}}return }...

【Spring Boot 入门三】Spring Boot与数据库集成 - 构建数据驱动的应用

一、引言 在之前的文章中&#xff0c;我们已经对Spring Boot有了初步的认识&#xff0c;了解了如何构建第一个Spring Boot应用&#xff0c;以及如何通过配置文件来掌控应用的设置。这些知识为我们进一步探索Spring Boot与数据库的集成奠定了坚实的基础。 数据库是现代应用的核…...

Web 服务器与动态脚本语言通信的接口协议有哪些

Web 服务器与动态脚本语言通信的接口协议主要有以下几种&#xff1a; 一、FastCGI&#xff08;Fast Common Gateway Interface&#xff09; 特点&#xff1a;使用持久进程处理请求&#xff0c;减少了进程启动和关闭的开销&#xff0c;提高了性能和可扩展性。多个请求可由同一个…...

ESXI识别服务器磁盘,虚拟机显示无效

ESXI识别服务器磁盘&#xff0c;虚拟机显示无效 系统意外断电识别不到磁盘的情况下可以管理-》硬件-》搜索磁盘名称&#xff0c;选择切换直通&#xff0c;则虚拟机正常。...

【C++】 vector 迭代器失效问题

【C】 vector 迭代器失效问题 一. 迭代器失效问题分析二. 对于vector可能会导致其迭代器失效的操作有&#xff1a;1. 会引起其底层空间改变的操作&#xff0c;都有可能是迭代器失效2. 指定位置元素的删除操作--erase3. Linux下&#xff0c;g编译器对迭代器失效的检测并不是非常…...

【Spring基础3】- Spring的入门程序

目录 3-1 Spring的下载3-2 Spring的 jar 包3-3 第一个 Spring程序第一步&#xff1a;添加spring context的依赖&#xff0c;pom.xml配置如下第二步&#xff1a;添加junit依赖第三步&#xff1a;定义bean&#xff1a;User第四步&#xff1a;编写spring的配置文件&#xff1a;bea…...

golang学习笔记22-面向对象(四):接口【重要】

本节也是GO核心部分&#xff0c;很重要。 注&#xff1a;由于导包语句已经在19讲&#xff08;笔记19&#xff1a;面向对象的引入&#xff09;展示过了&#xff0c;所以这里就不展示了。 一、定义与实现 (1)接口中可以定义一组方法&#xff0c;但不需要实现&#xff0c;不需要…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

python打卡day49@浙大疏锦行

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...

学习 Hooks【Plan - June - Week 2】

一、React API React 提供了丰富的核心 API&#xff0c;用于创建组件、管理状态、处理副作用、优化性能等。本文档总结 React 常用的 API 方法和组件。 1. React 核心 API React.createElement(type, props, …children) 用于创建 React 元素&#xff0c;JSX 会被编译成该函数…...