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

Kotlin Flow 冷流

协程:Flow

1、Flow是什么?

  1. 处理异步事件流
  2. 可取消:通过取消协程取消Flow
  3. 组合操作符:复杂逻辑处理
  4. 缓冲和背压:发送和接收时用不同速度处理,实现流量控制、避免数据丢失

2、传统事件处理方案:同步、sequence、异步delay

    // 1、同步:fun getList() = listOf(100, 200, 300, 400, 500, 600)val job = GlobalScope.launch {getList().forEach{println(it)}}job.join()// 2、异步: 在SequenceScope中,禁止自己调用挂起,除了库内部的函数yield可以挂起fun getSequcence() = sequence{for(item in 0..1000){// 不允许使用 delayyield(item) // 可以做到协程间切换}}val job2 = GlobalScope.launch {getSequcence().forEach {println(it)}}job2.join()// 3、异步: 挂起,但没有协作suspend fun getSuspendSequcence(): List<Int> {delay(1000)return listOf(100, 200, 300, 400, 500, 600)}val job3 = GlobalScope.launch {getSuspendSequcence().forEach {println(it)}}job3.join()

flow

1、flow的作用

  1. RxJava和Flow完全一样
  2. 替代LiveData
// 1、类似Observablesuspend fun getFlow() = flow{for(item in 1..8){// 发射emit(item)}}val job = GlobalScope.launch {// 2、类似RxJava消费,subscribe === Observer消费getFlow().collect{println(it)}}job.join()

2、getFlow()可以不用suspend修饰,更自由

    fun getFlow() = flow{for(item in 1..8){// 发射emit(item)}}

替换LiveData

1、Flow可以完全替换LiveData ===> LiveData

// Step 1 : 网络请求
fun fetchData() = flow{for(item in 1..100){emit("get Json String = $item")}
}
// Step 2 : ViewModel抛弃LiveData使用flow
class MyViewModel: ViewModel(){val dataFlow: Flow<String> = fetchData()
}// Step 3 : 订阅,并且collect返回数据
class MyFragment: Fragment(){val viewModel: MyViewModel by viewModels()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)lifecycleScope.launch {viewModel.dataFlow.collect{// 在Lifecycle的CoroutineScope中,订阅冷流println(it)}}}
}

flowOn

1、Kotlin的flowOn替代了subscribeOn, 对上游进行了切换 ====> RxJava

  1. 不再需要observeOn,在需要的线程collect即可
fun main() = runBlocking<Unit> { // 顶级协程launch(Dispatchers.Default) {NetworkRequest.uploadRequestAction().flowOn(Dispatchers.IO) // flow运行在IO线程池,默认是main。替换了subscribeOn
//            .observeOn(Dispatchers.Default) // 不再需要observeOn,在需要的线程collect即可.collect {println("$it%") // 显示下载进度}}// flowOn是给上游还是下游切换?// 都是给上游
}object NetworkRequest {fun uploadRequestAction() = flow {println("uploadRequestAction thread:${Thread.currentThread().name}")for (item in 1..100) {delay(100)emit(item) // 反馈文件上传进度}}
}

冷流

1、flow和RxJava都是冷流

发射源简化

1、简化发射源 ===> 高阶函数

  1. 一切对象、函数都可以toFlow转换为flow
// 1、无参非挂起函数 toFlow
private fun<T> (()->T).toFlow() = flow{emit(invoke())
}// 使用:val r: () -> String = ::getFlowValuer.toFlow().collect { println(it) }// 2、String toFlow
//private fun String.toFlow() = flow{
//    emit(this@toFlow) // this@toFlow有markdown错误
//}// 使用:"String".toFlow().collect { println(it) }// 3、无参挂起函数
private fun <OUTPUT> (suspend () -> OUTPUT).toFlow() = flow {emit(invoke())
}// 使用:::getFlowValueSuspend.toFlow().collect { println(it) }// 4、所有集合toFlow
private fun <E> Iterable<E>.toFlow() = flow {this@toFlow.forEach { emit(it) }
}// 使用:listOf(1, 2, 3, 4, 5, 6).toFlow().collect { println(it) }setOf(100, 200, 300, 400, 500, 600).toFlow().collect { println(it) }// 5、sequence的toFlow
private fun <T> Sequence<T>.toFlow() = flow {this@toFlow.forEach { emit(it) }
}// 使用sequence {yield("Derry1")yield("Derry2")yield("Derry3")}.toFlow().collect { println(it) }// 6、Array系列处理
//private fun <T> Array<T>.toFlow() = flow {
//    // this@toFlow.forEach { emit(it) }
//    repeat(this@toFlow.size) {
//        emit(this@toFlow[it])
//    }
//}
//
//private fun IntArray.toFlow() = flow {
//    for (i in this@toFlow) {
//        emit(i)
//    }
//}
//
//private fun LongArray.toFlow() = flow {
//    for (i in this@toFlow) {
//        emit(i)
//    }
//}// 7、Range
// 注意第4步,就已经覆盖Range的情况
private fun IntRange.toFlow() = flow {this@toFlow.forEach { emit(it) }
}private fun LongRange.toFlow() = flow {this@toFlow.forEach { emit(it) }
}

vararg和flowOf

1、可变参数实现单个数据或者多个数据都可以转为flow

private fun <T> flows(vararg value: T) = flow{value.forEach {emit(it)}
}

使用

    flows("Hello").collect{ println(it) }flows(1,2,3,4,5).collect{ println(it) }

2、使用官方的flowOf

    flowOf("Hello").collect{ println(it) }flowOf(1,2,3,4,5).collect{ println(it) }

withContext

1、协程中上游不可以使用withContext,只能使用flowOn

  1. 上下文保存机制
  2. 使用withContext会报错

launchIn

1、launchIn的作用

  1. 发射区域flowOn
  2. 收集区域launchIn:选择下游协程,需要用onEach打印数据
// 发射源区域
fun getFlowValue() =listOf(100, 200, 300, 400, 500, 600).asFlow().onEach { delay(2000) }.flowOn(Dispatchers.Default)
// 收集消费区域val job = getFlowValue().onEach { println("thread:${Thread.currentThread().name}   $it")  }.launchIn(CoroutineScope(Dispatchers.IO + CoroutineName("自定义协程"))) // 打开水龙头job.join() // 需要等待执行完成,不然外面main执行结束了。

输出结果

thread:DefaultDispatcher-worker-3 @自定义协程#2   100
thread:DefaultDispatcher-worker-1 @自定义协程#2   200
thread:DefaultDispatcher-worker-1 @自定义协程#2   300
thread:DefaultDispatcher-worker-1 @自定义协程#2   400
thread:DefaultDispatcher-worker-1 @自定义协程#2   500
thread:DefaultDispatcher-worker-1 @自定义协程#2   600

cancellable

1、协程取消,会导致Flow管道流也会取消。每次都delay 1000,可以正确检测异常

fun getFlow() = flow {(1..10).forEach { emit(it) }
}.onEach { delay(1000) }
    getFlow().collect {println(it)if (it == 5) cancel()}

输出结果

1
2
3
4
5
Exception in thread "main" kotlinx.coroutines.JobCancellationException

检测

2、cancellable:取消不及时,速度太快了,增加监测机制

    (1..10).asFlow().collect {println(it)if (it == 5) cancel()}// 会输出1~10,才抛出异常(1..10).asFlow().cancellable().collect {println(it)if (it == 5) cancel()}// 可以正确捕获到

背压

buffer

1、背压是什么?

  1. 数据产生速度 >>> 数据消费速度,消耗过多时间
  2. 可能OOM
// 数据过多,会导致消费不来
fun getFlow() = flow {(1..10).forEach {delay(500L)emit(it) // 一秒钟发射一个 一秒钟发射一个 ....println("生成了:$it thread:${Thread.currentThread().name}")}
}// 消费慢val t = measureTimeMillis {getFlow().collect {delay(1000L)println("消费了:$it thread:${Thread.currentThread().name}")}}println("上游 下游 共 消耗:$t 时间")// 共消耗15495ms
// 都在一个线程处理,按顺序,放一个取一个

2、buffer:设立缓冲区,减少背压的数量【解决办法一】

fun getFlow() = flow {(1..10).forEach {delay(500L)emit(it) // 一秒钟发射一个 一秒钟发射一个 ....println("生成了:$it thread:${Thread.currentThread().name}")}
}.buffer(100) // 设置缓冲区,减少 背压// 共消耗11272ms

3、flowOn(Dispatchers.IO):另一个线程处理【解决办法二】

  1. 可以和buffer一起使用
fun getFlow() = flow {(1..10).forEach {delay(500L)emit(it) // 一秒钟发射一个 一秒钟发射一个 ....println("生成了:$it thread:${Thread.currentThread().name}")}
}.buffer(100) // 设置缓冲区,减少 背压.flowOn(Dispatchers.IO)
// 共消耗11001ms

conflate

1、conflate作用:只消费当前认为最新的值,会丢失部分信息

    val t = measureTimeMillis {getFlow().conflate().collect{delay(1000L)println("消费了:$it thread:${Thread.currentThread().name}")}}println("上游 下游 共 消耗:$t 时间")
// 共消耗7303ms

collectLatest

1、collectLatest:只收集最新值,速度大幅度提升

    val t = measureTimeMillis {getFlow().collectLatest {delay(1000L)println("消费了:$it thread:${Thread.currentThread().name}")}}println("上游 下游 共 消耗:$t 时间")
// 共消耗6869ms

transform

1、transform将上游数据转换后交给下游 ====> LiveData

    listOf(100, 200, 300, 400, 500, 600).asFlow().transform {this.emit("你好啊数字$it")}.collect { println(it) }

take

1、take限制发送的长度,只要前面几个

    listOf(100, 200, 300, 400, 500, 600).asFlow().take(4).collect { println(it) }

2、自定义take

  1. 对Flow扩展,调用collect收集结果
  2. 用结果构造出flow
fun <INPUT> Flow<INPUT>.myTake(number:Int):Flow<INPUT>{require(number > 0){"Request element count 0 show be positive"}return flow {var i = 0collect{// collect收集的n个数据,构造了flow{}if(i++ < number){return@collect emit(it)}}}
}

reduce

末端操作符:适合累加

  1. reduce参数p1 = 上一次运算返回的最后一行
  2. 下面代码实现:1+2+3+4+…+100 = 5050
    val r = (1..100).asFlow().reduce { p1, p2 ->val result = p1 + p2result}println(r)

fliter:过滤

    (100..200).toFlow().filter { it % 50 == 0 }.map { "map result:$it" }.collect{ println(it) }

zip

1、zip合并Flow

fun getNames() = listOf("杜子腾", "史珍香", "刘奋").asFlow().onEach { delay(1000) }
fun getAges() = arrayOf(30, 40, 50).asFlow().onEach { delay(2000) }// 合并 组合 操作符 zipgetNames().zip(getAges()) { p1, p2 ->"name:$p1, age:$p2"}.collect {println(it)}输出:
name:杜子腾, age:30
name:史珍香, age:40
name:刘奋, age:50 

2、zip合并的两个Flow数据长度不一样会怎么办?

fun getNames() = listOf("杜子腾", "史珍香", "刘奋").asFlow().onEach { delay(1000) }
fun getAges() = arrayOf(30, 40, 50, 60, 70).asFlow().onEach { delay(2000) }zip之后输出结果:会抛弃不匹配的信息6070
name:杜子腾, age:30
name:史珍香, age:40
name:刘奋, age:50 

map

转换

flatmap

1、flatMapxxx作用是展平

  1. 不展平相当于 Flow嵌套,如:Flow<Flow<String>>
  2. 需要两次收集:collect { it.collect { a -> println(a)} }手动展平
// 不展平相当于 Flow嵌套,如:Flow<Flow<String>> 
// 这里发送两次事件,属于Flow
fun runWork(inputValue:Int) = flow {emit("$inputValue 号员工开始工作了")delay(1000L)emit("$inputValue 号员工结束工作了")
}(1..6).asFlow().onEach { delay(1000L)}.map { runWork(it) } // Flow<Flow<String>> // Flow嵌套.collect { it.collect { a -> println(a)} }
    // 展平 操作符 flatMapgetNumbers().onEach { delay(1000L)}
//        .flatMap {  } // 已经废弃.flatMapConcat { runWork(it) }// .flatMapMerge { runWork(it) }// .flatMapLatest { runWork(it) }.collect { println(it) }

2、flatMapConcat:拼接,常用
3、flatMapMerge
4、flatMapLatest

merge

1、flow合并,执行,并且获得结果

  1. 数据请求函数
data class Home(val info1: String, val info2: String)data class HomeRequestResponseResultData(val code: Int, val msg: String, val home: Home)// 请求本地加载首页数据
fun CoroutineScope.getHomeLocalData(userName: String) = async (Dispatchers.IO) {delay(3000)Home("数据1...", "数据1...")
}// 请求网络服务器加载首页数据
fun CoroutineScope.getHomeRemoteData(userName: String) = async (Dispatchers.IO) {delay(6000)Home("数据3...", "数据4...")
}
  1. map + merge,合并Flow,collect触发冷流
// 流程// 1.把多个函数 拿过来// 2.组装成协程// 3.包装成FLow// 4.Flow合并 得到 结果coroutineScope {val r = listOf(::getHomeLocalData, ::getHomeRemoteData) // 1.把多个函数 拿过来.map {it("Derry用户") //it.call("Derry用户") 需要引入Kotlin反射 2.组装成协程,调用}.map {flow { emit(it.await()) }// 3.包装成FLow}val r2 = r.merge() // 4.Flow合并 得到 结果r2.collect { println(it) }}

异常

catch

捕获上游的异常

  1. 用声明式
    flow {listOf(100).forEach { value ->emit(value)throw KotlinNullPointerException("上游抛出了异常")}}.catch {println("e:$it")emit(200)}.onEach { delay(1000L) }.collect { println(it) }
  1. Flow是流式的,catch不能捕获下游的异常

onCompletion

1、Flow正常结束,声明式

getNumbers().onCompletion { println("协程Flow结束了") }.collect{println(it)}

2、onCompletion来捕获异常结束:上游和下游都可以

// 上游getNumbers2().onCompletion {if (it != null) { // 非正常结束  是异常结束println("上游 发生了异常 $it")}}.catch { println("被catch到了 上游 发生了异常 $it") }  // .catch是能 捕获到 上游 抛出的异常, 异常的传递过程.collect { println(it) }

3、异常总结

  1. 上游的异常抛出,可以使用 声明式
  2. 下游的异常抛出,可以使用 命令式
  3. onCompletion(声明式) 上游 与 下游 的异常信息,都能够知道 能够得到
  4. onCompletion(声明式) 正常的结束 还是 异常的结束,都能知道
  5. finally 能够知道正常的结束(命令式)

相关文章:

Kotlin Flow 冷流

协程&#xff1a;Flow 1、Flow是什么&#xff1f; 处理异步事件流可取消&#xff1a;通过取消协程取消Flow组合操作符&#xff1a;复杂逻辑处理缓冲和背压&#xff1a;发送和接收时用不同速度处理&#xff0c;实现流量控制、避免数据丢失 2、传统事件处理方案&#xff1a;同…...

Android Socket使用TCP协议实现手机投屏

本节主要通过实战来了解Socket在TCP/IP协议中充当的是一个什么角色&#xff0c;有什么作用。通过Socket使用TCP协议实现局域网内手机A充当服务端&#xff0c;手机B充当客户端&#xff0c;手机B连接手机A&#xff0c;手机A获取屏幕数据转化为Bitmap&#xff0c;通过Socket传递个…...

【云原生,k8s】Helm应用包管理器介绍

目录 一、为什么需要Helm&#xff1f; &#xff08;一&#xff09;Helm介绍 &#xff08;二&#xff09;Helm有3个重要概念&#xff1a; &#xff08;三&#xff09;Helm特点 二、Helm V3变化 &#xff08;一&#xff09;架构变化 &#xff08;二&#xff09;自动创建名…...

两个内网之间的linux服务器如何互相登录?快解析内网穿透

如果两个内网之间的linux服务器需要互相登录&#xff0c;或需要互相访问内网某个端口&#xff0c;担忧没有公网IP&#xff0c;可以使用的方法有 ngrok, 但并不方便&#xff0c;我们只需两条 SSH 命令即可。 SSH 内网端口转发实战SSH 内网端口转发实战 先给出本文主角&…...

sql server 存储过程 set ansi_nulls set quoted_identifier,out 、output

SQL-92 标准要求在对空值(NULL) 进行等于 () 或不等于 (<>) 比较时取值为 FALSE。 当 SET ANSI_NULLS 为 ON 时&#xff0c;即使 column_name 中包含空值&#xff0c;使用 WHERE column_name NULL 的 SELECT 语句仍返回零行。即使 column_name 中包含非空值&#xff0c…...

1046:判断一个数能否同时被3和5整除

【题目描述】 判断一个数n 能否同时被3和5整除&#xff0c;如果能同时被3和5整除输出YES&#xff0c;否则输出NO。 【输入】 输入一行&#xff0c;包含一个整数n。&#xff08; -1,000,000 < n < 1,000,000&#xff09; 【输出】 输出一行&#xff0c;如果能同时被3…...

优漫动游零基础如何学习好UI设计

智能时代的来临&#xff0c;很多企业都越来越注重用户体验这一块&#xff0c;想要有一个吸引用户的好页面&#xff0c;UI设计师岗位不可或缺&#xff0c;如今越来越多的人想要学习UI设计技术&#xff0c;那么对于零基础小白如何学习好UI设计呢? 零基础小白如何学习好UI设计…...

Android岗位技能实训室建设方案

一 、系统概述 Android岗位技能作为新一代信息技术的重点和促进信息消费的核心产业&#xff0c;已成为我国转变信息服务业的发展新热点&#xff1a;成为信息通信领域发展最快、市场潜力最大的业务领域。互联网尤其是移动互联网&#xff0c;以其巨大的信息交换能力和快速渗透能力…...

Mysql系列:Mysql5.7编译安装--系统环境:Centos7 / CentOS9 Stream

Mysql系列&#xff1a;Mysql5.7编译安装 系统环境&#xff1a;Centos7 / CentOS9 Stream 1&#xff1a;下载mysql源码包 https://dev.mysql.com/downloads/mysql/5.7.htmldownloads 选择MySQL Community Server>source_code>Generic Linux (Architecture Independent)…...

Docker容器与虚拟化技术:Dockerfile部署LNMP

目录 一、理论 1.LNMP架构 2.背景 3.Dockerfile部署LNMP 3.构建Nginx镜像 4.构建MySQL容器 5.构建PHP镜像 6.启动 wordpress 服务 二、实验 1.环境准备 2.构建Nginx镜像 3.构建MySQL容器 4.构建PHP镜像 5.启动 wordpress 服务 三、问题 1.构建nginx镜像报错 …...

elementUI date-picker 日期格式转为 2023/08/08格式

<el-form-item label"基线日期:" prop"baselineDate"><el-date-pickertype"date"v-model"form.baselineDate"placeholder"选择日期"format"yyyy/MM/dd"change"(date, type) > changeTime(date, …...

生成式 AI 在泛娱乐行业的应用场景实践 – 助力风格化视频内容创作

感谢大家阅读《生成式 AI 行业解决方案指南》系列博客&#xff0c;全系列分为 4 篇&#xff0c;将为大家系统地介绍生成式 AI 解决方案指南及其在电商、游戏、泛娱乐行业中的典型场景及应用实践。目录如下&#xff1a; 《生成式 AI 行业解决方案指南与部署指南》《生成式 AI 在…...

elementPlus——图标引入+批量注册全局组件——基础积累

因为我们要根据路由配置对应的图标&#xff0c;也要为了后续方便更改。因此我们将所有的图标注册为全局组件。&#xff08;使用之前将分页器以及矢量图注册全局组件的自定义插件&#xff09;&#xff08;所有图标全局注册的方法element-plus文档中已给出&#xff09; 全局注册…...

国标GB28181安防视频平台EasyGBS显示状态正常,却无法播放该如何解决?

国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台&#xff0c;可实现的视频功能包括&#xff1a;实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强&#xff0c;支持将…...

TIOVX:opencv的Mat类图像零拷贝转为openvx的vx_image格式,通过Not节点无效果问题记录

问题描述 代码中&#xff0c;创建了一个opencv的Mat图像(并打印了所有的像素值)&#xff0c;然后通过vxCreateImageFromHandle函数将Mat图像转为了vx_image图像(通过映射的方式打印了所有的像素值&#xff0c;通过日志可以看出与之前打印相同)。然后创建graph&#xff0c;将其作…...

变压器故障诊断(python代码,逻辑回归/SVM/KNN三种方法同时使用,有详细中文注释)

视频效果&#xff1a;变压器三种方法下故障诊断Python代码_哔哩哔哩_bilibili代码运行要求&#xff1a;tensorflow版本>2.4.0,Python>3.6.0即可&#xff0c;无需修改数据路径。 1.数据集介绍&#xff1a; 采集数据的设备照片 变压器在电力系统中扮演着非常重要的角色。…...

ASEMI探索整流桥GBU814的独特优势和应用领域

编辑-Z 整流桥GBU814在众多电子元件中独树一帜&#xff0c;可在多种设备中发挥其重要作用。作为一款集高效性能和可靠稳定性于一身的整流桥&#xff0c;GBU814已在全球范围内赢得了广泛的好评。在这篇文章中&#xff0c;我们将详细介绍GBU814整流桥的优势和应用领域。 让我们首…...

js脚本自动化之葫芦娃

什么是葫芦娃? 贵州特产平台(扶贫助农平台)有很多,但都大同小异,就连模样都像一个娘生的,所以戏称为葫芦娃平台 #小程序://航旅黔购/1nkYlNRVzm0Gg9x #小程序://贵旅优品/7zz6mtnSVgDfyqa #小程序://新联惠购/ibFdsuhWqIbczEd #小程序://贵盐黔品/u2TgExCUdkavrFe #小程…...

从零基础到精通IT:探索高效学习路径与成功案例

文章目录 导语&#xff1a;第一步&#xff1a;明确学习目标与方向选择适合的IT方向设定具体的学习目标咨询和调研 第二步&#xff1a;系统学习基础知识选择适合的编程语言学习数据结构和算法掌握操作系统和计算机网络基础 第三步&#xff1a;实践项目锻炼技能选择合适的项目编写…...

2023.8.8巨人网络数据开发工程师面试复盘

1 概述 问题一览 总体感觉良好&#xff0c;通过面试官的介绍可知这个岗位偏向离线数仓。 1.自我介绍 2.询问了其中一段实习经历 3.讲下你说用过的Linux命令 4.讲下HIVE的内部表和外部表有什么不同 *5.讲下你使用过的Hive函数&#xff08;好好在复习下多准备几个吧&#xff09…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...