【iOS】多线程原理

目录

前言

基本概念及原理

线程、进程与队列

线程的定义:

进程的定义:

线程与进程之间的联系与区别:

线程和runloop的关系

影响任务执行速度的因素

多线程

多线程生命周期

线程池的原理

iOS中多线程的实现方式

线程安全问题

互斥锁

自旋锁

对比GCD和NSOperation

NSThread

GCD

函数

队列

函数与队列的不同组合

串行队列 + 同步派发

​编辑串行队列 + 异步派发

​编辑

并发队列 + 同步派发

​编辑

并发队列 + 异步派发

主队列 + 同步函数

主队列 + 异步派发

dispatch_after

dispatch_once

dispatch_apply

dispatch_group_t

dispatch_group_async + dispatch_group_notify

dispatch_group_enter + dispatch_group_leave + dispatch_group_notify

dispatch_barrier_sync & dispatch_barrier_async

dispatch_semaphore_t

dispatch_source

NSOperation

NSInvocationOperation

NSBlockOperation

NSOperationQueue

设置优先级

设置并发数

设置依赖


前言

多线程在iOS的开发中起到了非常重要的作用,笔者在之前已经有学习过关于GCD的知识了,但是当时学得迷迷糊糊,正好借学习多线程底层原理的机会,来在学习多线程的同时对之前的知识做一个复习

基本概念及原理

线程、进程与队列

线程的定义:
  • 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行

  • 进程想要执行任务,必须得有线程,进程至少要有一条线程

  • 程序启动会默认开启一条线程,这条线程被成为主线程UI线程

进程的定义:
  • 进程是指在系统中正在运行的一个应用程序,如微信、支付宝app都是一个进程

  • 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间内

通俗地说,可以理解为:进程是线程的容器,而线程用来执行任务。在iOS中是单进程开发,一个进程就是一个app,进程之间是相互独立的,如支付宝、微信、qq等,这些都是属于不同的进程。

线程与进程之间的联系与区别:
  • 地址空间:同一进程线程共享本进程的地址空间,而进程之间则是独立的地址空间

  • 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的

  • 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉,所以多进程要比多线程健壮

  • 进程切换时,消耗的资源大、效率高.所以设计到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程而不能用进程

  • 线程是处理器调度的基本单位,但进程不是

  • 线程没有地址空间,线程包含在进程地址空间中

线程和runloop的关系
  • runloop与线程是一一对应的 —— 一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里

  • runloop是来管理线程的 —— 当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务

  • runloop在第一次获取时被创建,在线程结束时被销毁

    • 对于主线程来说,runloop在程序一启动就默认创建好了

    • 对于子线程来说,runloop是懒加载的 —— 只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调

影响任务执行速度的因素

以下因素都会对任务的执行速度造成影响:

  • cpu的调度

  • 线程的执行速率

  • 队列情况

  • 任务执行的复杂度

  • 任务的优先级

多线程

多线程生命周期

多线程的生命周期主要分为5部分:新建 - 就绪 - 运行 - 阻塞 - 死亡

  • 新建:实例化线程对象

  • 就绪:线程对象调用start方法,将线程对象加入可调度线程池等待CPU的调用,即调用start方法,并不会立即执行,进入就绪状态,需要等待一段时间,经CPU调度后才执行,也就是从就绪状态进入运行状态

  • 运行:CPU 负责调度可调度线程池中线程的执行。在线程执行完成之前,其状态可能会在就绪和运行之间来回切换.就绪和运行之间的状态变化由CPU负责,程序员不能干预

  • 阻塞:当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行。sleepForTimeInterval(休眠指定时长),sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥锁)

  • 死亡:正常死亡,即线程执行完毕。非正常死亡,即当满足某个条件后,在线程内部(或者主线程中)终止执行(调用exit方法等退出)

处于运行中的线程拥有一段可以执行的时间(称为时间片):

  • 如果时间片用尽,线程就会进入就绪状态队列

  • 如果时间片没有用尽,且需要开始等待某事件,就会进入阻塞状态队列

  • 等待事件发生后,线程又会重新进入就绪状态队列

  • 每当一个线程离开运行,即执行完毕或者强制退出后,会重新从就绪状态队列选择一个线程继续执行

关于线程的exit和cancel方法:

  • exit:一旦强行终止线程,后续的所有代码都不会执行

  • cancel:取消当前线程,但是不能取消正在执行的线程

线程池的原理

可以看到主要分为以下四步:

  1. 判断核心线程池是否都正在执行任务:

    • 返回NO,创建新的工作线程去执行

    • 返回YES,进行第二步

  2. 判断线程池工作队列是否已经饱满:

    • 返回NO,将任务存储到工作队列,等待CPU调度

    • 返回YES,进入第三步

  3. 判断线程池中的线程是否都处于执行状态

    • 返回NO,安排可调度线程池中空闲的线程去执行任务

    • 返回YES,进入第四步

  4. 交给饱和策略去执行,主要有以下四种:

    • AbortPolicy:直接抛出RejectedExecutionExeception异常来阻止系统正常运行

    • CallerRunsPolicy:将任务回退到调用者

    • DisOldestPolicy:丢掉等待最久的任务

    • DisCardPolicy:直接丢弃任务

iOS中多线程的实现方式

iOS中多线程的实现方式主要有四种:pthread、NSThread、GCD、NSOperation

线程安全问题

当多个线程同时访问一块内存,容易引发数据错乱和数据安全问题,有以下两种解决方案:

  • 互斥锁(即同步锁):@synchronized

  • 自旋锁

互斥锁
  • 保证锁内的代码,同一时间,只有一条线程能够执行!

  • 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差!

  • 加了互斥锁的代码,当新线程访问时,如果发现其他线程正在执行锁定的代码,新线程就会进入休眠

  • 能够加锁的是任意 NSObject 对象,但必须是 NSObject 对象

  • 锁对象必须保证所有线程都能访问

  • 单点加锁时推荐使用 self

自旋锁
  • 自旋锁与互斥锁类似,但它不是通过休眠使线程阻塞,而是在获取锁之前一直处于忙等(即原地打转,称为自旋)阻塞状态

  • 使用场景:锁持有的时间短,且线程不希望在重新调度上花太多成本时,就需要使用自旋锁,属性修饰符atomic,本身就有一把自旋锁

  • 加入了自旋锁,当新线程访问代码时,如果发现有其他线程正在锁定代码,新线程会用死循环的方法,一直等待锁定的代码执行完成,即不停的尝试执行代码,比较消耗性能

  • atomic 本身就有一把锁(自旋锁)

iOS开发的建议:

  • 所有属性都声明为 nonatomic

  • 尽量避免多线程抢夺同一块资源 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

对比GCD和NSOperation

GCDNSOperation的关系如下:

  • GCD是面向底层的C语言的API

  • NSOperation是用GCD封装构建的,是GCD的高级抽象

GCD和NSOperation的对比如下:

  • GCD执行效率更高,而且由于队列中执行的是由block构成的任务,这是一个轻量级的数据结构 —— 写起来更加方便

  • GCD只支持FIFO的队列,而NSOpration可以设置最大并发数、设置优先级、添加依赖关系等调整执行顺序

  • NSOpration甚至可以跨队列设置依赖关系,但是GCD只能通过设置串行队列,或者在队列内添加barrier任务才能控制执行顺序,较为复杂

  • NSOperation支持KVO(面向对象)可以检测operation是否正在执行、是否结束、是否取消(如果是自定义的NSOperation 子类,需要手动触发KVO通知)

NSThread

NSthread是苹果官方提供面向对象的线程操作技术,是对thread的上层封装,比较偏向于底层。

通过NSThread创建线程的方式主要有以下三种方式:

  • 通过init初始化方式创建

  • 通过detachNewThreadSelector构造器方式创建

  • 通过performSelector...方法创建,主要是用于获取主线程,以及后台线程

NSThread常用的类方法有以下:

  • currentThread:获取当前线程

  • sleep...:阻塞线程

  • exit:退出线程

  • mainThread:获取主线程

GCD

GCD就是Grand Central Dispatch,它是纯 C 语言。关于GCD,笔者之前已经有博客详细介绍过它的概念和接口以及用法了。这里对于GCD的简单概念就不重复赘述了,详情可以点击笔者这篇博客——OC高级编程之GCD。这里就直接对GCD的函数与队列来进行一个再梳理和复习吧

函数

GCD中执行任务的方式有两种,同步执行异步执行,分别对应同步函数dispatch_sync异步函数dispatch_async

  • 同步执行,对应同步函数dispatch_sync

    • 必须等待当前语句执行完毕,才会执行下一条语句

    • 不会开启线程,即不具备开启新线程的能力

    • 在当前线程中执行block任务

  • 异步执行,对应异步函数dispatch_async

    • 不用等待当前语句执行完毕,就可以执行下一条语句

    • 会开启线程执行block任务,即具备开启新线程的能力(但并不一定开启新线程,这个与任务所指定的队列类型有关)

    • 异步是多线程的代名词

综上所述,两种执行方式的主要区别有两点:

  • 是否等待队列的任务执行完毕

  • 是否具备开启新线程的能力

队列

多线程中所说的队列Dispatch Queue)是指执行任务的等待队列,即用来存放任务的队列.队列是一种特殊的线性表,遵循先进先出(FIFO)原则,即新任务总是被插入到队尾,而任务的读取从队首开始读取.每读取一个任务,则动队列中释放一个任务。而队列又分为串行队列并发队列

串行队列:每次只有一个任务被执行,等待上一个任务执行完毕再执行下一个,即只开启一个线程

并发队列:一次可以并发执行多个任务,即开启多个线程,并同时执行任务

函数与队列的不同组合

串行队列 + 同步派发

任务一个接一个地在当前线程执行,不会开辟新线程

串行队列 + 异步派发

任务一个接一个地执行,但是会开辟新线程

并发队列 + 同步派发

任务一个接一个地执行,不开辟线程

并发队列 + 异步派发

任务乱序进行并且会开辟新线程

主队列 + 同步函数

任务互相等待,造成死锁

为什么这样会造成死锁,这里分析一下原因:

主队列在执行任务执行到同步block时,会将block的任务加入到主队列,但由于主队列是串行队列,因此block的任务要等主线程执行完block才可以执行(因为当前主线程中任务还没有执行完,任务应该是进行到执行block了),而执行block其实就是执行block里的任务(即NSLog),主线程等着block里这个任务执行完才执行完,这样就使得任务之间互相等待,从而造成了死锁崩溃

死锁

  • 主线程因为同步函数的原因等着先执行任务

  • 主队列等着主线程的任务执行完毕再执行自己的任务

  • 主队列和主线程相互等待会造成死锁

主队列 + 异步派发

主队列是一个特殊的串行队列,它虽然是串行队列,但是其异步派发不会开辟新线程,而是将任务安排到主线程的下一个运行循环(Run Loop)周期执行

dispatch_after

dispatch_after表示在队列中的block延迟执行,确切地说是延迟将block加入到队列

dispatch_once

dispatch_once可以保证在app运行期间,block中的代码只执行一次,可以用来创建单例

dispatch_apply

dispatch_apply将指定的block追加到指定的队列中重复执行,并等到全部的处理执行结束(相当于线程安全的for循环)

应用场景:在拉取网络数据后提前计算出各个控件的大小,防止绘制时计算,提高表单滑动流畅性

dispatch_group_t

dispatch_group_t:调度组将任务分组执行,能监听任务组完成,并设置等待时间

应用场景:多个接口请求之后刷新页面

dispatch_group_async + dispatch_group_notify

dispatch_group_notifydispatch_group_async执行结束之后会受收到通知

dispatch_group_enter + dispatch_group_leave + dispatch_group_notify

dispatch_group_enterdispatch_group_leave成对出现,使进出组的逻辑更加清晰

在此基础上还可以使用 dispatch_group_wait

这里dispatch_group_wait这个函数第一个参数表示要等待的调度组,第二个参数表示要等多久(如果设置为DISPATCH_TIME_NOW表示不等待直接判定是否执行完毕,如果设置为DISPATCH_TIME_FOREVER表示阻塞当前调度组直到调度组执行完毕)

这个函数的返回值为long类型,如果返回值为0,表示在指定时间内调度组完成了任务;如果不为0,表示在指定时间内调度组没有按时完成任务

dispatch_barrier_sync & dispatch_barrier_async

栅栏函数,主要使用在并发队列,串行队列使用栅栏函数没什么意义。

栅栏函数即:等栅栏前追加到队列中的任务执行完毕后,再将栅栏后的任务追加到队列中。 简而言之,就是先执行栅栏前任务,再执行栅栏任务,最后执行栅栏后任务

可以看到如果没有栅栏,按照主线程的派发顺序,任务2延迟1s,任务1延迟2s,应该是先完成任务2再完成任务1的,但是因为这里有栅栏函数,所以这里任务执行的顺序变为:先执行栅栏前的任务1,再执行栅栏任务,然后执行栅栏后的任务2

  • dispatch_barrier_syncdispatch_barrier_async的作用相同,区别在于是否阻塞线程

注意⚠️

1.尽量使用自定义的并发队列

  • 使用全局队列起不到栅栏函数的作用

  • 使用全局队列时由于对全局队列造成堵塞,可能致使系统其他调用全局队列的地方也堵塞从而导致崩溃(并不是只有你在使用这个队列)

2.栅栏函数只能控制同一并发队列:打个比方,平时在使用AFNetworking做网络请求时为什么不能用栅栏函数起到同步锁堵塞的效果,因为AFNetworking内部有自己的队列(也就是说栅栏函数不能跨队列作用)

dispatch_semaphore_t

dispatch_semaphore_t表示信号量,可以用来控制GCD最大并发数

  • dispatch_semaphore_create():创建信号量

  • dispatch_semaphore_wait():等待信号量,信号量减1。当信号量< 0时会阻塞当前线程,根据传入的等待时间决定接下来的操作——如果永久等待将等到信号(signal)才执行下去

  • dispatch_semaphore_signal():释放信号量,信号量加1。当信号量>= 0 会执行wait之后的代码

比如用信号量来代替栅栏函数使这段代码按序输出:

使用信号量的API来改写的话就是这样的:

如果当创建信号量时传入值为1又会怎么样呢?

  • i=0时有可能先打印,也可能会先发出wait信号量-1,但是wait之后信号量为0不会阻塞线程,所以进入i=1

  • i=1时有可能先打印,也可能会先发出wait信号量-1,但是wait之后信号量为-1阻塞线程,等待signal再执行下去

结论:

  • 创建信号量时传入值为1时,可以通过两次才堵塞

  • 传入值为2时,可以通过三次才堵塞

dispatch_source

dispatch_source是一种基本的数据类型,可以用来监听一些底层的系统事件

  • Timer Dispatch Source:定时器事件源,用来生成周期性的通知或回调

  • Signal Dispatch Source:监听信号事件源,当有UNIX信号发生时会通知

  • Descriptor Dispatch Source:监听文件或socket事件源,当文件或socket数据发生变化时会通知

  • Process Dispatch Source:监听进程事件源,与进程相关的事件通知

  • Mach port Dispatch Source:监听Mach端口事件源

  • Custom Dispatch Source:监听自定义事件源

主要使用的API:

  • dispatch_source_create: 创建事件源

  • dispatch_source_set_event_handler: 设置数据源回调

  • dispatch_source_merge_data: 设置事件源数据

  • dispatch_source_get_data: 获取事件源数据

  • dispatch_resume: 继续

  • dispatch_suspend: 挂起

  • dispatch_cancle: 取消

比如通过dispatch_source来实现定时器,在开发中经常使用NSTimer来实现定时逻辑,但是NSTimier是依赖Runloop的,而Runloop可以运行在不同的模式下,如果NSTimer添加在一一种模式下,而Runloop运行在其他模式下,定时器就挂起了;又如果Runloop在阻塞状态,那么NSTimer的触发时间就会推迟到下一个Runloop周。因此NSTimer在计时上会有误差,而GCD计时器不依赖Runloop,计时精度高很多

需要注意⚠️:

  • GCDTimer需要强持有,否则出了作用域立即释放,也就没有了事件回调

  • GCDTimer默认是挂起状态,需要手动激活

  • GCDTimer没有repeat,需要封装来增加标志位控制

  • GCDTimer如果存在循环引用,使用weak+strong或者提前调用dispatch_source_cancel取消timer

  • dispatch_resumedispatch_suspend调用次数需要平衡

  • source挂起状态下,如果直接设置source = nil或者重新创建source都会造成crash。正确的方式是在激活状态下调用dispatch_source_cancel(source)释放当前的source

NSOperation

NSOperation是个抽象类,依赖于子类NSInvocationOperationNSBlockOperation去实现

NSInvocationOperation

也可以直接处理事务,不添加隐性队列

NSBlockOperation

NSInvocationOperationNSBlockOperation两者的区别在于:

  • 前者类似target形式

  • 后者类似block形式——函数式编程,业务逻辑代码可读性更高

NSOperationQueue是异步执行的,所以任务一任务二的完成顺序不确定

通过addExecutionBlock这个方法可以让NSBlockOperation实现多线程

NSBlockOperation创建时block中的任务是在主线程执行,而运用addExecutionBlock加入的任务是在子线程执行的(准确地来说,创建时block中的任务在start调用发生的线程执行)(当Operation没有添加到队列,而是通过start调用时)

NSOperationQueue

NSOperationQueue有两种队列:主队列、其他队列

  • 主队列:主队列上的任务是在主线程执行的

  • 其他队列(非主队列):加入到非主队列中的任务默认就是并发,开启多线程

通过类方法mainQueue可以得到主队列

设置优先级

NSOperation设置优先级只会让CPU有更高的几率调用,不是说设置高就一定全部先完成

通过以下是否让高优先级任务休眠的任务执行顺序即可看出这一点:

不使用sleep:

使用sleep:

设置并发数

在NSOperation中不需使用信号量,直接设置maxConcurrentOperationCount就可以控制并发数,来控制单次出队列去执行的任务数

设置依赖

NSOperation中添加依赖能很好的控制任务执行的先后顺序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93319.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93319.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

药房发药的“时间密码”:同步时钟用药安全?

在医院的药房里&#xff0c;每一粒药片的流转都暗藏“时间密码”。从药品入库到患者服药&#xff0c;时间记录的精确性直接关乎生命安全。一旦时间数据出现偏差&#xff0c;轻则导致用药争议&#xff0c;重则引发医疗事故。近年来&#xff0c;随着医疗数字化进程加速&#xff0…

UI-TARS-Desktop 深度解析:下一代智能自动化桌面平台

目录 1. 产品概述 2. 核心功能与技术架构 2.1 关键技术 2.2 功能亮点 3. 竞品对比分析 4. 部署与成本分析 4.1 部署方案 4.2 隐性成本 5. 商业化前景 5.1 目标市场 5.2 盈利模式 5.3 风险挑战 6. 未来演进方向 7. 总结 1. 产品概述 UI-TARS-Desktop 是一款基于A…

STM32L051同时处理Alarm A和Alarm B中断

同时处理Alarm A和Alarm B中断 当同时启用Alarm A和Alarm B时&#xff0c;需要在中断处理程序中准确判断是哪个闹钟触发了中断。以下是完整的解决方案&#xff1a; 中断判断与处理流程 1. 在RTC中断服务程序中判断中断源 // stm32l0xx_it.c void RTC_IRQHandler(void) {/* USER…

OpenCV---morphologyEx形态学操作

在计算机视觉与图像处理领域&#xff0c;形态学操作是一种基于图像形状的非线性处理方法&#xff0c;广泛应用于噪声去除、边缘检测、目标分割等任务。OpenCV提供的morphologyEx函数是形态学操作的“瑞士军刀”&#xff0c;它整合了多种高级形态学运算&#xff0c;能够实现开运…

RuoYi-Cloud 接入 Sentinel 的 3 种限流方式

场景&#xff1a; 服务&#xff1a;ruoyi-robot&#xff08;对外接口统一在 /external/gs/**&#xff09; 网关&#xff1a;ruoyi-gateway&#xff08;转发到 ruoyi-robot&#xff09; 注册/配置&#xff1a;Nacos 流控&#xff1a;Sentinel 1.8.x 控制台 Dashboard&#x…

快速搭建python HTTP Server测试环境

这里用python http.server搭建一个api测试环境&#xff0c;自定义请求处理程序&#xff0c;以模拟不同api相应。 1 服务代码 /api/data&#xff0c;端口8000&#xff0c;GET 返回json数据为"{"message": "This is a sample API response"}" 代…

Docker容器定时任务时区Bug导致业务异常的环境变量配置解决方案

Docker容器定时任务时区Bug导致业务异常的环境变量配置解决方案 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都是…

解锁Dify与MySQL的深度融合:MCP魔法开启数据新旅程

文章目录解锁Dify与MySQL的深度融合&#xff1a;MCP魔法开启数据新旅程引言&#xff1a;技术融合的奇妙开篇认识主角&#xff1a;Dify、MCP 与 MySQL&#xff08;一&#xff09;Dify&#xff1a;大语言模型应用开发利器&#xff08;二&#xff09;MCP&#xff1a;连接的桥梁&am…

杂记 02

1 WSL安装 WSL的安装遇到了问题&#xff0c;睡醒起来发现电脑蓝屏了&#xff0c;linux系统没装好&#xff0c;但是好像大部分开发环境都是linux下需要的&#xff0c;先这样用一下吧&#xff0c;到时候再说。可以问下前辈开发细节&#xff0c;主要是网络代理问题&#xff0c;保…

剧本杀小程序系统开发:重构推理娱乐生态

在娱乐产业蓬勃发展的今天&#xff0c;推理娱乐作为一种充满智慧和挑战的娱乐形式&#xff0c;受到了越来越多人的喜爱。剧本杀&#xff0c;作为推理娱乐的代表之一&#xff0c;正以其独特的魅力吸引着大量玩家。而剧本杀小程序系统开发&#xff0c;则为推理娱乐生态的重构带来…

力扣习题:基本计算器

本片内容我们将针对于一个力扣中的一道很经典的习题&#xff1a;基本计算器。 这道题目十分经典&#xff0c;在很多大厂的面试题中都有出现过 因此我们将进一步来学习 该题目代码已经上传作者的个人gitee&#xff1a;CPP 学习代码库: C代码库新库&#xff0c;旧有C仓库满员了喜…

Element用法---Loading 加载

仅供参考 文章目录一、加载动画二、Loading 组件1、指令调用 Loading2、服务调用 Loading一、加载动画 当我们打开某个页面时&#xff0c;如果需要加载的数据很多或者网络很差&#xff0c;页面加载就会非常缓慢&#xff0c;中间可能会很长时间显示空白&#xff0c;那么就需要加…

飞算AI 3.2.0实战评测:10分钟搭建企业级RBAC权限系统

飞算AI 3.2.0实战评测&#xff1a;10分钟搭建企业级RBAC权限系统 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都…

事务的四大特性

事务&#xff08;Transaction&#xff09;是数据库管理系统&#xff08;DBMS&#xff09;中用于保证数据操作正确性和一致性的核心机制。事务的特性通常用 ACID 四个字母概括&#xff0c;分别代表 原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&…

WIN11系统下Open3D 0.19.0支持GPU的python版本

前往Open 3D官网下载https://github.com/isl-org/Open3D下载对应版本的源码。 根据官方手册利用cmake进行编译&安装&#xff0c;其中需要修改一些代码适应于win 11系统&#xff0c;编译时间较长需要耐心等待。最后&#xff0c;安装结果如下图&#xff0c;搞了四天&#xff…

ICCV 2025 | 4相机干掉480机位?CMU MonoFusion高斯泼溅重构4D人体!

​​​​ 近日&#xff0c;卡内基梅隆大学&#xff08;Carnegie Mellon University&#xff09;的研究团队在动态场景重建领域取得重要进展。其发表于ICCV 2025的论文《MonoFusion: Sparse-View 4D Reconstruction via Monocular Fusion》提出创新方法MonoFusion 。该方法突破常…

ADB 无线调试连接(Windows + WSL 环境)

gradle wrapper --gradle-version 8.4 Windows WSL 成功连接 Android 设备&#xff08;用于 ./gradlew installDebug&#xff09;的完整过程总结&#xff1a;✅ ADB 无线调试连接过程&#xff08;Windows WSL 环境&#xff09; &#x1f4cc; 目标&#xff1a;从 WSL 中通过 …

【.net core】【wetercloud】处理前端项目免登陆,且从前端项目跳转至系统内时的问题

1.前端项目访问后台内容时免登陆&#xff08;一般用于后台接口需要校验登陆时&#xff09;处理思路&#xff1a;将后台用户的登陆校验令牌信息在用户登录后添加至前端项目访问地址的参数列表中&#xff0c;如&#xff1a;https://yourdomain/Home/Index#/https://yourdomain/vi…

设备 AI 知识库,管理效率新飞跃

在设备管理领域&#xff0c;高效解决设备故障、合理规划维护工作对企业生产运营至关重要。易点易动设备管理系统新推出的设备 AI 知识库&#xff0c;为提升管理效率带来了新契机。设备 AI 知识库集成先进的人工智能技术&#xff0c;是设备管理领域的创新应用。易点易动设备管理…

C#绘制斐波那契螺旋

Fabonacci 数列&#xff0c;也就是”兔子数列“&#xff0c; 如果第一项为0的话&#xff0c;就是&#xff0c; 0&#xff0c;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……