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

Kotlin - 协程结构化并发Structured Concurrency

前言

KotlinProject LeadRoman Elizarov一片文章https://elizarov.medium.com/structured-concurrency-722d765aa952介绍了Structured Concurrency发展背景。相对Kotlin1.1时代后来新增Structured Concurrency理念,也就是我们现在所熟悉的协程版本所具备的特性,解决各种复杂业务场景例如协程嵌套、异步等等使用方式所面临生命周期管理问题本文通过梳理源码试图理解Structured Concurrency的具体含义实现原理

概念理解

常见业务场景如下

suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) }val deferred2 = async { loadImage(name2) }return combineImages(deferred1.await(), deferred2.await())
}

deferred1deferred2都是异步执行最终需要二者执行结果合并后返回如果此时其中一个loadImage执行异常或者主动取消很难通知另一个LoadImage及时停止执行释放资源

或者如下场景:

val scope = CoroutineScope(Job())
    scope.launch {printLog("launch1")
        launch {delay(20000)printLog("launch1-1")}printLog("launch1 done")cancel()}

外层launch执行最后希望cancel内部所有子协程没有Structrued Concurrency特性时候要实现这种逻辑需要类似使用线程处理方式Structrued Concurrency特性可以让我们cancel外层协程自动cancel里面所有的子协程

这就是所谓的对协程生命周期管理为了能够将所有协程生命周期完全管理起来Kotlin使用CoroutineScope

Coroutines are always related to some local scope in your application, which is an entity with a limited life-time, like a UI element.

CoroutineScope相当于圈定一个空间所有协程这个空间里面执行这样所有协程声明周期可以通过CoroutineScope进行管理

实现原理

我们知道launch都是一个JobJobCoroutineScope关系如下

再次根据这个例子这种关系如何实现

val scope = CoroutineScope(Job())
    scope.launch {printLog("launch1")
        launch {delay(20000)printLog("launch1-1")}printLog("launch1 done")cancel()}

首先新建CoroutineScope(Job())

kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\CoroutineScope.ktpublic fun CoroutineScope(context: CoroutineContext): CoroutineScope =ContextScope(if (context[Job] != null) context else context + Job())
internal class ContextScope(context: CoroutineContext) : CoroutineScope {override val coroutineContext: CoroutineContext = context// CoroutineScope is used intentionally for user-friendly representationoverride fun toString(): String = "CoroutineScope(coroutineContext=$coroutineContext)"
}

CoroutineScope本身是一个接口这里CoroutineScope不是构造函数而是一个顶层函数这里两个关注点

context[Job]context + Job()

所有JobCoroutineDispatcher继承于CoroutineContext因此CoroutineScope函数参数我们可以新建一个Job(), 也可以一个CoroutineDispatcherJob()为例看下实现

public interface Job : CoroutineContext.Element {/**
     * Key for [Job] instance in the coroutine context.
     */public companion object Key : CoroutineContext.Key<Job>

Job继承于CoroutineContext.Element

public interface Element : CoroutineContext {/**
         * A key of this coroutine context element.
         */public val key: Key<*>public override operator fun <E : Element> get(key: Key<E>): E? =@Suppress("UNCHECKED_CAST")if (this.key == key) this as E else nullpublic override fun <R> fold(initial: R, operation: (R, Element) -> R): R =operation(initial, this)public override fun minusKey(key: Key<*>): CoroutineContext =if (this.key == key) EmptyCoroutineContext else this}

注意这里get函数返回值取决于keykey哪里赋值

Job也是一个接口Job()也是顶层函数

public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)
internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {

JobImp继承JobSupportJobSupportJob具体实现

public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob {final override val key: CoroutineContext.Key<*> get() = Job

可以看到key实际值Job

所以如果CoroutineScope(...)的参数传入Job(), context[Job]返回Job

那context + Job()代表什么

CoroutineContext接口声明看到plus操作符重载:

public operator fun plus(context: CoroutineContext): CoroutineContext =if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
            context.fold(this) { acc, element ->val removed = acc.minusKey(element.key)if (removed === EmptyCoroutineContext) element else {// make sure interceptor is always last in the context (and thus is fast to get when present)val interceptor = removed[ContinuationInterceptor]if (interceptor == null) CombinedContext(removed, element) else {val left = removed.minusKey(ContinuationInterceptor)if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) elseCombinedContext(CombinedContext(left, element), interceptor)}}}

是将两个CoroutineContext合并成了CombinedContextCombinedContext本身一个CoroutineContext

综上 CoroutineScope时候如果传入一个Job使用这个Job如果没有传入Job(可能传入一个CoroutineDispatcher)新建一个Job然后Job赋值ContextScopecoroutineContext成员变量。

如此一来一个新建CoroutineScope关联一个顶层Job

使用launch创建一个协程:
kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\Builders.common.ktpublic fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {val newContext = newCoroutineContext(context)val coroutine = if (start.isLazy)LazyStandaloneCoroutine(newContext, block) elseStandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)return coroutine
}

首先launchCoroutineScope扩展函数也就是只能在CoroutineScope创建协程newCoroutineContext(context)

public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {val combined = foldCopies(coroutineContext, context, true)val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combinedreturn if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default else debug
}

这里contextEmptyCoroutineContextcoroutineContext刚才CoroutineScope(Job())传入的顶层Job经过foldCopies返回combined可以看做顶层Job的封装。return语句中可以看到debug(即顶层Job)加上debug + Dispatchers.Default这就是为什么默认运行Dispatchers.Default线程原因

创建newContext,如果start.isLazy构建LazyStandaloneCoroutine否则构建StandaloneCoroutinestart协程执行方式默认立即执行也可以懒加载执行具体kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\CoroutineStart.kt

这里构建的是默认StandaloneCoroutine

private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {override fun handleJobException(exception: Throwable): Boolean {handleCoroutineException(context, exception)return true}
}

parentContext参数传入刚才构建newContext也就顶层JobinitParentJob默认值true接着看下他的继承AbstractCoroutine

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {init {/*
         * Setup parent-child relationship between the parent in the context and the current coroutine.
         * It may cause this coroutine to become _cancelling_ if the parent is already cancelled.
         * It is dangerous to install parent-child relationship here if the coroutine class
         * operates its state from within onCancelled or onCancelling
         * (with exceptions for rx integrations that can't have any parent)
         */if (initParentJob) initParentJob(parentContext[Job])}

AbstractCoroutine继承了JobSupportJob也就是StandaloneCoroutine实际上构造一个Job看下这里initParentJob(parentContext[Job])parentContext进来顶层Job封装newContext这里取出Job传进initParentJob

protected fun initParentJob(parent: Job?) {
        assert { parentHandle == null }if (parent == null) {
            parentHandle = NonDisposableHandlereturn}
        parent.start() // make sure the parent is startedval handle = parent.attachChild(this)
        parentHandle = handle// now check our state _after_ registering (see tryFinalizeSimpleState order of actions)if (isCompleted) {
            handle.dispose()
            parentHandle = NonDisposableHandle // release it just in case, to aid GC}}

这里执行parent.attachChild(this)字面理解launch创建出来Job作为Child加入顶层的Job

关联父子Job

看下具体实现

kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\JobSupport.kt

public final override fun attachChild(child: ChildJob): ChildHandle {val node = ChildHandleNode(child).also { it.job = this }val added = tryPutNodeIntoList(node) { _, list ->// First, try to add a child along the cancellation handlersval addedBeforeCancellation = list.addLast(
                node,
                LIST_ON_COMPLETION_PERMISSION or LIST_CHILD_PERMISSION or LIST_CANCELLATION_PERMISSION)...
                node.invoke(rootCause)if (addedBeforeCompletion) {/** The root cause can't be null: since the earlier addition to the list failed, this means that
                     * the job was already cancelled or completed. */
                    assert { rootCause != null }true} else {/** No sense in retrying: we know it won't succeed, and we already invoked the handler. */return NonDisposableHandle}}}if (added) return node/** We can only end up here if [tryPutNodeIntoList] detected a final state. */
        node.invoke((state as? CompletedExceptionally)?.cause)return NonDisposableHandle}

首先构造一个ChildHandleNode

private class ChildHandleNode(@JvmField val childJob: ChildJob
) : JobNode(), ChildHandle {override val parent: Job get() = joboverride val onCancelling: Boolean get() = trueoverride fun invoke(cause: Throwable?) = childJob.parentCancelled(job)override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}

这里parent传入顶层JobchildJob是launch新建Job

tryPutNodeIntoList

private inline fun tryPutNodeIntoList(
        node: JobNode,
        tryAdd: (Incomplete, NodeList) -> Boolean): Boolean {
        loopOnState { state ->when (state) {is Empty -> { // EMPTY_X state -- no completion handlersif (state.isActive) {// try to move to the SINGLE stateif (_state.compareAndSet(state, node)) return true} elsepromoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine}is Incomplete -> when (val list = state.list) {null -> promoteSingleToNodeList(state as JobNode)else -> if (tryAdd(state, list)) return true}else -> return false}}}
private val _state = atomic<Any?>(if (active) EMPTY_ACTIVE else EMPTY_NEW)private inline fun loopOnState(block: (Any?) -> Unit): Nothing {while (true) {block(state)}}

state是什么

kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\JobSupport.ktprivate val EMPTY_NEW = Empty(false)
private val EMPTY_ACTIVE = Empty(true)

JobSupport维护一个状态机管理Job不同状态阶段这里EMPTY_NEW和 EMPTY_ACTIVE具体状态

private class Empty(override val isActive: Boolean) : Incomplete {override val list: NodeList? get() = nulloverride fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
}

其内维护一个list

简言之就是tryAdd(state, list)自己的state内的list传递调用tryPutNodeIntoList回头tryPutNodeIntoList

val addedBeforeCompletion = list.addLast(
                    node,
                    LIST_CHILD_PERMISSION or LIST_ON_COMPLETION_PERMISSION)

Job加到list

由此一来CoroutineScope构建Job树。

Job的执行

回到CoroutineScope.launch

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {val newContext = newCoroutineContext(context)val coroutine = if (start.isLazy)LazyStandaloneCoroutine(newContext, block) elseStandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)return coroutine
}

构建coroutine执行coroutine.start

public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {start(block, receiver, this)}
public enum class CoroutineStart {
...
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =when (this) {
            DEFAULT -> block.startCoroutineCancellable(receiver, completion)
            ATOMIC -> block.startCoroutine(receiver, completion)
            UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
            LAZY -> Unit // will start lazily}

在这里开始执行协程

Structured Concurrency的典型作用:协程的cancel

当执行scopecancel

public fun CoroutineScope.cancel(cause: CancellationException? = null) {val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}

是通过coroutineContext[Job]获取顶层Job然后执行cancel

kotlinx.coroutines-master\kotlinx-coroutines-core\common\src\JobSupport.kt
public override fun cancel(cause: CancellationException?) {cancelInternal(cause ?: defaultCancellationException())}public open fun cancelInternal(cause: Throwable) {cancelImpl(cause)}internal fun cancelImpl(cause: Any?): Boolean {var finalState: Any? = COMPLETING_ALREADYif (onCancelComplete) {// make sure it is completing, if cancelMakeCompleting returns state it means it had make it// completing and had recorded exception
            finalState = cancelMakeCompleting(cause)if (finalState === COMPLETING_WAITING_CHILDREN) return true}if (finalState === COMPLETING_ALREADY) {
            finalState = makeCancelling(cause)}return when {
            finalState === COMPLETING_ALREADY -> true
            finalState === COMPLETING_WAITING_CHILDREN -> true
            finalState === TOO_LATE_TO_CANCEL -> falseelse -> {afterCompletion(finalState)true}}}

makeCancelling为例

private fun makeCancelling(cause: Any?): Any? {var causeExceptionCache: Throwable? = null // lazily init result of createCauseException(cause)
        loopOnState { state ->when (state) {is Finishing -> { // already finishing -- collect exceptionsval notifyRootCause = synchronized(state) {if (state.isSealed) return TOO_LATE_TO_CANCEL // already sealed -- cannot add exception nor mark cancelled// add exception, do nothing is parent is cancelling child that is already being cancelledval wasCancelling = state.isCancelling // will notify if was not cancelling// Materialize missing exception if it is the first exception (otherwise -- don't)if (cause != null || !wasCancelling) {val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
                            state.addExceptionLocked(causeException)}// take cause for notification if was not in cancelling state before
                        state.rootCause.takeIf { !wasCancelling }}
                    notifyRootCause?.let { notifyCancelling(state.list, it) }return COMPLETING_ALREADY}is Incomplete -> {// Not yet finishing -- try to make it cancellingval causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }if (state.isActive) {// active state becomes cancellingif (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY} else {// non active state starts completingval finalState = tryMakeCompleting(state, CompletedExceptionally(causeException))when {
                            finalState === COMPLETING_ALREADY -> error("Cannot happen in $state")
                            finalState === COMPLETING_RETRY -> return@loopOnStateelse -> return finalState}}}else -> return TOO_LATE_TO_CANCEL // already complete}}}

假如协程运行执行tryMakeCancelling

private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
        assert { state !is Finishing } // only for non-finishing states
        assert { state.isActive } // only for active states// get state's list or else promote to list to correctly operate on child listsval list = getOrPromoteCancellingList(state) ?: return false// Create cancelling state (with rootCause!)val cancelling = Finishing(list, false, rootCause)if (!_state.compareAndSet(state, cancelling)) return false// Notify listenersnotifyCancelling(list, rootCause)return true}

state.compareAndSet进行状态机切换随后执行notifyCancelling

private fun notifyCancelling(list: NodeList, cause: Throwable) {// first cancel our own childrenonCancelling(cause)
        list.close(LIST_CANCELLATION_PERMISSION)notifyHandlers(list, cause) { it.onCancelling }// then cancel parentcancelParent(cause) // tentative cancellation -- does not matter if there is no parent}
private fun notifyCancelling(list: NodeList, cause: Throwable) {// first cancel our own childrenonCancelling(cause)
        list.close(LIST_CANCELLATION_PERMISSION)notifyHandlers(list, cause) { it.onCancelling }// then cancel parentcancelParent(cause) // tentative cancellation -- does not matter if there is no parent}private inline fun notifyHandlers(list: NodeList, cause: Throwable?, predicate: (JobNode) -> Boolean) {var exception: Throwable? = null
        list.forEach { node ->if (node is JobNode && predicate(node)) {try {
                    node.invoke(cause)} catch (ex: Throwable) {
                    exception?.apply { addSuppressed(ex) } ?: run {
                        exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)}}}}
        exception?.let { handleOnCompletionException(it) }

node.invoke(cause)实现

private class InvokeOnCancelling(private val handler: CompletionHandler
) : JobNode()  {// delegate handler shall be invoked at most once, so here is an additional flagprivate val _invoked = atomic(false)override val onCancelling get() = trueoverride fun invoke(cause: Throwable?) {if (_invoked.compareAndSet(expect = false, update = true)) handler.invoke(cause)}
}
private fun cancelParent(cause: Throwable): Boolean {// Is scoped coroutine -- don't propagate, will be rethrownif (isScopedCoroutine) return true/* CancellationException is considered "normal" and parent usually is not cancelled when child produces it.
         * This allow parent to cancel its children (normally) without being cancelled itself, unless
         * child crashes and produce some other exception during its completion.
         */val isCancellation = cause is CancellationExceptionval parent = parentHandle// No parent -- ignore CE, report other exceptions.if (parent === null || parent === NonDisposableHandle) {return isCancellation}// Notify parent but don't forget to check cancellationreturn parent.childCancelled(cause) || isCancellation}

将自己状态切换取消随后notifyHandlers通过遍历list通知自己children执行cancel最后通过cancelParent告知Job自己的分支cancel完毕

总结

  1. 所有协程运行CoroutineScope这种限定通过launchasyncrunBlock构建协程函数都是作为CoroutineScope扩展函数实现
  2. CoroutineScope创建过程中必定构建一个顶层Job(后者外部传入),通过coroutineContext与其关联
  3. 每个launch响应构建一个JobJob加入到Joblist由此维护了Job
  4. Structure Concurrency 具体实现 通过 维护 Job 的生命周期 完成

相关文章:

Kotlin - 协程结构化并发Structured Concurrency

前言 Kotlin的Project Lead&#xff0c;Roman Elizarov的一片文章https://elizarov.medium.com/structured-concurrency-722d765aa952介绍了Structured Concurrency发展的背景。相对Kotlin1.1时代&#xff0c;后来新增的Structured Concurrency理念&#xff0c;也就是我们现在所…...

新版国标GB28181设备端Android版EasyGBD支持国标GB28181-2022,支持语音对讲,支持位置上报,开源在Github

经过近3个月的迭代开发&#xff0c;新版本的国标GB28181设备端EasyGBD安卓Android版终于在昨天发布到Github了&#xff0c;最新的EasyGBD支持了国标GB28181-2022版&#xff0c;还支持了语音对讲、位置上报、本地录像等功能&#xff0c;比原有GB28181-2016版的EasyGBD更加高效、…...

豆包MarsCode测评:编程效率再提升

豆包MarsCode测评&#xff1a;编程效率再提升 本文正在参与豆包MarsCode AI 编程体验家活动 随着人工智能技术的发展&#xff0c;编程的方式也在悄然发生变化。最近&#xff0c;豆包推出的 AI 编程工具 MarsCode 在开发者社区引发了不小的关注。这是一款支持多种主流编程语言…...

二叉树 -- 堆(详解)

目录 1、堆的概念及结构 2、堆的实现(附代码) 2.1、向下调整算法建堆 3、堆的应用(附代码) 3.1、堆排序 3.2、TOP-K问题 1、堆的概念及结构 如果有一个关键码的集合K { k0&#xff0c;k1 &#xff0c;k2 &#xff0c;…&#xff0c;k(n-1) }&#xff0c;把它的所有元素…...

【Apache Paimon】-- 11 -- Flink 消费 kakfa 写 S3 File

目录 1、项目构建 2、项目新增和修改 2.1 pom.xml 新增依赖 2.2 本地测试或者 flink on k8s 时,新增 S3FileSystemFactory.java 第一步:创建包=org.apache.flink.fs.s3hadoop 第二步:新增 java 类 S3FileSystemFactory 特别注意 (1)本地测试时需要新增以下内容 (…...

SQL MID()

SQL中的MID()函数是一个用于从指定位置开始截取字符串中指定长度的子串的函数。这个函数在数据库查询和数据处理中经常被使用&#xff0c;特别是在需要从较长的文本字段中提取特定信息时。 MID()函数的基本语法是&#xff1a;SELECT MID(column_name, start, length) FROM tab…...

jsp | servlet | spring forEach读取不了对象List

导致这个问题的原因有很多的&#xff0c;这里讲到的只是原因之一 原因 taglib不认识forEach 解决办法 添加<% taglib uri"http://java.sun.com/jsp/jstl/core" prefix"c" %> &#xff08;我忘写这个东西了哈哈哈&#xff09;...

【ArcGIS Pro微课1000例】0063:处理无人机数据(空三、生成DOM、DSM、DTM)

使用ArcGIS Pro 正射拼接处理无人机数据流程化工具,不需要额外产品许可的支持,只需要桌面是高级版许可即可支持。ArcGIS Pro处理无人机摄影测量数据主要内容有:空三、生成DOM、DSM、DTM。 文章目录 一、创建映射项目二、提交自由空三三、添加控制点优化四、提交产品生产一、…...

【pytorch】深度学习计算

1 层和块 块由类&#xff08;class&#xff09;表示。它的任何子类都必须定义一个将其输入转换为输出的前向传播函数&#xff0c;并且必须存储任何必需的参数。注意&#xff0c;有些块不需要任何参数。最后&#xff0c;为了计算梯度&#xff0c;块必须具有反向传播函数。 1.1…...

详解磁盘IO、网络IO、零拷贝IO、BIO、NIO、AIO、IO多路复用(select、poll、epoll)

1、什么是I/O 在计算机操作系统中&#xff0c;所谓的I/O就是输入&#xff08;Input&#xff09;和输出&#xff08;Output&#xff09;&#xff0c;也可以理解为读&#xff08;Read&#xff09;和写&#xff08;Write)&#xff0c;针对不同的对象&#xff0c;I/O模式可以划分为…...

VPN技术-GRE隧道的配置

GRE隧道的配置 1&#xff0c; 在AR1上配置DHCP接口地址池&#xff0c;AR3上配置DHCP全局地址池 2&#xff0c; PC1获取的IP地址为10.10.10.253&#xff0c;PC2获取的IP地址为10.10.30.253 3&#xff0c;通过ip route-static将目的地址为10.10.30.253的流量引入到Tunnel #配…...

【spring-cloud-gateway总结】

文章目录 什么是gateway如何导入gateway依赖路由配置gateway配置断路器导包配置 什么是gateway 在微服务架构中&#xff0c;gateway网关是一个服务&#xff0c;它作为系统的唯一入口点&#xff0c;处理所有的客户端请求&#xff0c;然后将这些请求路由到适当的服务。提供了几个…...

数组相关简单算法

目录 1. 数据结构与算法 2. 数组中涉及的算法 2.1 2.2 数值型数组相关运算 2.3 数组赋值 2.4 数组复制/反转 2.5 数组查找 2.6 排序 1. 数据结构与算法 《数据结构与算法》是大学些许专业的必修或选修课&#xff0c;主要包含两方面知识&#xff1a; &#xff08;1&#…...

在VBA中结合正则表达式和查找功能给文档添加交叉连接

在VBA中搜索文本有两种方式可用&#xff0c;一种是利用Range.Find对象&#xff08;更常见的形式可能是Selection.Find&#xff0c;Selection是Range的子类&#xff0c;Selection.Find其实就是特殊的Range.Find&#xff09;&#xff0c;另一种方法是利用正则表达式&#xff0c;但…...

动手学深度学习-多层感知机-7前向传播、反向传播和计算图

目录 前向传播 前向传播计算图 反向传播 训练神经网络 小结 我们已经学习了如何用小批量随机梯度下降训练模型。 然而当实现该算法时&#xff0c;我们只考虑了通过前向传播&#xff08;forward propagation&#xff09;所涉及的计算。 在计算梯度时&#xff0c;我们只调用…...

【Python】基于Python的CI/CD工具链:实现自动化构建与发布

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在现代软件开发中,持续集成(CI)和持续交付(CD)已经成为提高开发效率和软件质量的重要实践。CI/CD流程帮助开发团队自动化构建、测试、…...

FPGA-PS端编程1:

目标 在小梅哥的zynq 7015上&#xff0c;完成以下目标&#xff1a; 读取 S1 按键的电平&#xff0c; 当 S1 按键为按下状态时&#xff0c;驱动 PS LED 以 1S 的频率闪烁(注意理解 1S 的频率闪烁和 1S的时间翻转两种描述之间的差别)&#xff0c; 当 S1 释放后&#xff0c;停止…...

自制数据库迁移工具-C版-06-HappySunshineV1.5-(支持南大Gbase8a、PostgreSQL、达梦DM)

目录 一、环境信息 二、简述 三、架构图 四、升级点 五、支持功能 六、后续计划支持功能 七、安装包下载地址 八、配置参数介绍 九、安装步骤 1、用户创建 2、安装包解压 3、环境变量配置 4、环境变量生效 5、动态库链接检验 &#xff08;1&#xff09;HsManage…...

了解RPC

本文来自智谱清言 --------- RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种允许程序调用位于远程计算机上的子程序或服务的技术。这种技术使得构建分布式计算变得更加容易&#xff0c;因为它提供了强大的远程调用能力&#xff0c;同时保持…...

centos7 安装docker

文章目录 介绍docker特点安装1.前提准备2.下载1.移除旧版docker命令2.切换centos7的镜像源3.配置docker yum源4.安装最新docker5.输入命令验证docker 安装是否成功6.配置docker 镜像加速7.设置为开机自启 总结 介绍 ‌Docker‌是一种开源的容器化平台&#xff0c;旨在简化应用…...

Docker 入门:如何使用 Docker 容器化 AI 项目(一)

引言 在人工智能&#xff08;AI&#xff09;项目的开发和部署过程中&#xff0c;环境配置和依赖管理往往是开发者遇到的挑战之一。开发者通常需要在不同的机器上运行同样的代码&#xff0c;确保每个人使用的环境一致&#xff0c;才能避免 “在我的机器上可以运行”的尴尬问题。…...

LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读

LLMs之rStar&#xff1a;《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读 导读&#xff1a;这篇论文提出了一种名为rStar的自我博弈互推理方法&#xff0c;用于增强小型语言模型 (SLMs) 的推理能力&#xff0c;无需微调或依赖更强大的模型。rStar…...

【RK3588 Linux 5.x 内核编程】-内核中断与ThreadedIRQ

内核中断与ThreadedIRQ 文章目录 内核中断与ThreadedIRQ1、Threaded IRQ介绍2、Threaded IRQ相关API3、驱动实现4、驱动验证当 Interrupt 触发时,Interrupt handler 应该执行得非常快,它不应该运行更多的时间(它不应该执行耗时的任务)。 如果我们有执行更多任务的中断处理程…...

Message Processing With Spring Integration高级应用:自定义消息通道与端点

一、Spring Integration 简介 Spring Integration 是 Spring 框架的扩展&#xff0c;支持企业集成模式&#xff08;EIP&#xff09;&#xff0c;提供轻量级的消息处理功能&#xff0c;帮助开发者构建可维护、可测试的企业集成解决方案。 核心目标&#xff1a; 提供简单的模型…...

S32K324 MCAL中的Postbuild和PreCompile使用

文章目录 前言Postbuild和PreCompile的概念MCAL中配置差异总结 前言 之前一直看到MCAL配置中有这个Postbuild和PreCompile的配置&#xff0c;但是不太清楚这两个的区别和使用方法。最近在使用中出现了相关问题&#xff0c;本文介绍一下MCAL中这两种配置的区别和使用。 Postbu…...

kubeadm_k8s_v1.31高可用部署教程

kubeadm_k8s_v1.31高可用部署教程 实验环境部署拓扑图**部署署架构****Load Balance****Control plane node****Worker node****资源分配&#xff08;8台虚拟机&#xff09;**集群列表 前置准备关闭swap开启ipv4转发更多设置 1、Verify the MAC address and product_uuid are u…...

【AI日记】24.12.22 容忍与自由 | 环境因素和个人因素

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 内容&#xff1a;看 OpenAi 这周的发布会和其他 AI 新闻&#xff0c;大佬视频时间&#xff1a;3 小时 读书 书名&#xff1a;富兰克林自传时间&#xff1a;1 小时评估&#xff1a;读完&#xff0c;总体…...

【Java基础面试题030】Java和Go的区别?

回答重点 可以从语言的设计理念、并发模型、内存管理、生态系统与应用场景来说&#xff1a; 1&#xff09;语言设计理念&#xff1a; Java&#xff1a;Java是一种面向对象编程语言&#xff0c;强调继承、多态和封装等OOP特性。它运行在Java虚拟机&#xff08;JVM&#xff09…...

学习嵩山版《Java 开发手册》:编程规约 - 常量定义(P5)

概述 《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结&#xff0c;他旨在提升开发效率和代码质量 《Java 开发手册》是一本极具价值的 Java 开发规范指南&#xff0c;对于提升开发者的综合素质和代码质量具有重要意义 学习《Java 开发手册》是一个提升 Jav…...

洛谷 P1595 信封问题 C语言递归

题目描述 某人写了 n 封信和 n 个信封&#xff0c;如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。 输入格式 一个信封数 n&#xff0c;保证 n≤20。 输出格式 一个整数&#xff0c;代表有多少种情况。 输入输出样例 输入 #1 2 输出 #1 1 输入 #2 …...