异步编程技术

异步编程技术

这里将介绍不同的异步编程实现。

作为程序员,我们都面临着一个问题,就是如何不让我们的程序阻塞。无论我们是桌面开发,移动开发,甚至服务端开发。
有很多不同的实现来解决这个问题,包括:

-Threading
-Callbacks
-Futures, Promises
-Reactive Extensions
-Coroutines

我们先简明的看下前四种实现方式。

Threading

到目前为止,线程是最为程序员所知的一种避免阻塞应用的实现。

fun postItem(item: Item) {
    val token = preparePost()
    val post = submitPost(token, item)
    processPost(post)
}

fun preparePost(): Token {
    // makes a request and consequently blocks the main thread
    return token
}

我们假设 preparePost 方法是一个很耗时的方法,这样的话,它将会阻塞用户交互。我们可以把这个方法放在一个单独的线程中,这样就避免了阻塞UI线程。这是一个非常常见的技术,但是它有一系列的缺点:

-线程很耗资源,它们要切换上下文
-线程数量有限,操作系统能启动的线程数量是有限的,对于服务端来说,这是一个很大的瓶颈
-线程并不总是可用的,比如JavaScript就不支持线程
-编写好的代码并不容易,容易出现各种竞争条件,会陷入并发编程苦海

Callbacks

简单来说,就是把函数作为参数传递给另一个函数,一旦本身函数执行完成,就执行传参的函数。

fun postItem(item: Item) {
    preparePostAsync { token -> 
        submitPostAsync(token, item) { post -> 
            processPost(post)
        }
    }
}

fun preparePostAsync(callback: (Token) -> Unit) {
    // make request and return immediately 
    // arrange callback to be invoked later
}

这个看起来相对优雅的解决了问题,但是同样有几个问题:

-对于多重嵌套回调,这将是一个非常难理解的方式
-错误处理变得很复杂,对于多重嵌套,错误的传递和处理变得很复杂

Callbacks在事件循环结构的语言中比较常见,比如JavaScript,但更多的码农倾向于其它的解决方案,比如promises或者reactive extensions.

Futures, Promises

Futures,Promises,不同的平台或语言有不同的叫法,它是承若将在某个点返回一个叫Promise的对象,简略操作如下所示:

fun postItem(item: Item) {
    preparePostAsync() 
        .thenCompose { token -> 
            submitPostAsync(token, item)
        }
        .thenAccept { post -> 
            processPost(post)
        }
         
}

fun preparePostAsync(): Promise {
    // makes request an returns a promise that is completed later
    return promise 
}

这个方式对于我们来说,有一点挑战,比如说:

-不同的编程模型。和callback类似,从上而下的链式回调。传统的编程结构,比如循环,错误处理都不在有效
-不同的平台有不同的API
-具体的返回类型,返回类型并不是我们实际的数据结构
-错误处理变得复杂。错误的传递处理并不清晰明确。

Reactive Extensions

响应式扩展(Rx)被Eik Meijer引入C#,虽然它确实存在于.NET平台,但直到被Netflix移植到Java平台,才逐渐开始成为主流。从这起,各个平台实现自己的响应式扩展,包括JavaScript(RxJS)。

Rx的背后思想是把数据当作来处理,并且该可以被观测。实际上,Rx只是观察者模式,带有一系列的扩展来操作数据。

Rx和Futures很类似,但不同的是,我们可以认为Future返回一个直接的元素,而Rx返回流。另一方面,它提供了一中新的编程模型,就像所宣传的口号那样:

everything is a stream, and it’s observable

这意味着有不同的解决方法,并且提供了不同的思路去写异步代码。相比于Futures不同的是,Rx被移植到多个平台,我们有着一致的API体验。包括C#,Java,JavaScript,或者其它实现了Rx的语言。

此外,Rx引入了更友好的错误处理方式。

Coroutines

Kotlin异步编程的方式是使用coroutines,这是一种可挂起的执行方式,它可以在某一点挂起,之后从这点恢复继续执行。

对于码农来说,使用协程方法编写异步代码和编写同步代码没有什么不同,这中编码模型并不存在什么挑战。

举个栗子:

fun postItem(item: Item) {
    launch {
        val token = preparePost()
        val post = submitPost(token, item)
        processPost(post)
    }
}

suspend fun preparePost(): Token {
    // makes a request and suspends the coroutine
    return suspendCoroutine { /* ... */ } 
}

这段代码将会执行耗时工作,但并不阻塞主线程。带有 suspend 修饰的 preparePost 方法就是被成为可挂起函数。就像上面描述的那样,它可以在某个点挂起,然后从该点恢复执行。

-函数签名保持了一致,仅增加了 suspend 修饰符。返回值也是我们想要的类型
-编写代码和我们以前一样,并不需要特殊的语法
-编程模型和API保持了一样,我们可以继续使用循环,异常处理,不需要学习新的api集
-它是平台无关的,无论是运行在JVM,JavaScript平台,还是其它,我们写法都是一样的,编译器负责适配各个平台。

Coroutines并不是Kotlin发明的新概念,它们已经存在了几十年了,并且在其它语言非常流行,比如Go语言。需要注意的是,虽然Kotlin实现了Coroutines,但大多数功能都是在库函数里边。事实上,除了 suspend 关键词,没有其它的关键词被引入到语言中。这个和C#有着较大的不同,C#引入 asyncawait 做为语法的一部分。而对于Kotlin,这些只是库函数。

你可能感兴趣的:(Android)