iOS并发之协程

1.简单介绍一下协程的前世今生
协程(英语:coroutine)马尔文·康威于1958年发明了术语“coroutine”并用于构建汇编程序 ,关于协程最初的出版解说在1963年发表。
协程的概念在60年代就已经提出,目前在服务端中应用比较广泛,在高并发场景下使用极其合适,可以极大降低单机的线程数,提升单机的连接和处理能力,直到近些年随着前端扮演的角色越来越重要,业务越来越复杂,越来越多的回调地狱,让前端代码的可读性越来越差,所以协程才逐渐的展露头脚,比如在WWDC21 推出了Swift 5.5 支持异步编程 \ kotlin\ 以及ES7引入了 async/await。

2、协程的基本概念
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。从技术的角度来说,“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程非常类似于线程。但是协程是协作式多任务的,而线程典型是抢占式多任务的。这意味着协程提供并发性而非并行性。


132fe937-0939-4b92-8db3-c665f04ce531.png

3、演示进程和线程的买票代码,进行性能差异对比。带着问题去思考什么是线程的并发,什么是协程的并发?

非抢占式:
无需系统调用
作为对比,多线程执行一般需要通过系统调用去分配线程进行执行
协程是线程安全的,无需锁
挂起执行时:
保存寄存器和栈
不影响其他协程执行
恢复执行:
恢复之前的寄存器和栈
无缝切换回之前的执行逻辑

协程的优点:
(1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责
任,同时,协程也失去了标准线程使用多CPU的能力)
(2)无需原子操作锁定及同步的开销
(3)方便切换控制流,简化编程模型
(4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点:
(进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序。

一、GCD
a、线程之间的切换消耗

什么是线程上下文的切换?多任务系统往往需要同时执行多道作业。作业数往往大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了让用户感觉这些任务正在同时进行,操作系统的设计者巧妙地利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能,但同时也带来了保存现场和加载现场的直接消耗。
040005361425252.png

真正干活的不是线程,而是CPU。线程越多,干活不一定越快。
b、锁的消耗

线程的管理,线程的管理也是资源的管理,因为多个线程会共享一个进程内的资源。


381412-20151210224200715-688806242.jpeg

二、swift 协程
a、await\async
在 WWDC21 中 Swift 盼来了 async/await,作为现代编程语言的标志之一,async/await 可以让我们像编写常规代码一样,轻松地编写异步代码,这样能更直观且更安全地表达我们的思路。async/await 是整个 Swift 结构化并发的基础。

   Task.init {
  // 插入await关键字之后,调用异步函数work()
            await self.work()
       }
//在异步函数work()中,出发异步函数 buy(),async 函数可以调用其他 async 函数,也可以直接调用普通的同步函数
 func work() async {
        let start = CACurrentMediaTime()
        for _ in 0 ..< ticketTotal {
            let ticket = await self.buy()
            print(ticket)
        }
        let end = CACurrentMediaTime()
        print("协程-测量时间:\(end - start)")
    }
//Continuation函数会等待异步回调结束。
 func buy() async -> Int {
        await withUnsafeContinuation { continuation in
            self.asynBuyTicket { ticket in
                continuation.resume(returning: ticket)
            }
        }
    }

b、actor
Swift 5.5 中的并发模型旨在提供一个安全的编程模型,可以静态检测数据竞争和其他常见的并发错误。结构化并发为函数和闭包提供多线程竞争安全性保障。该模型适用于许多常见的设计模式,包括并行映射和并发回调模式等,但仅限于处理由闭包捕获的状态。
为此 Swift 5.5 引入了新的并发模型 Actor , 它通过数据隔离保护内部的数据,确保在给定时间只有一个线程可以访问该数据。
状态私有:外部无法直接去访问 Actor 的内部数据
只能接收和处理消息:外部想要与 Actor 通信只能发送消息
每个 Actor一次只执行一个消息:Actor 内部在一个消息队列中处理消息,保证了线程安全

94d2a794-882b-4717-a6dd-e3f9bdbdd2fc.png
actor Ticket {
    var number = ticketTotal
    var start = CACurrentMediaTime()
    var end = CACurrentMediaTime()

    func buy() {
        if number == ticketTotal {
            start = CACurrentMediaTime()
        }
        if number > 0 {
            number -= 1
            print((number))
        }
        if number == 0 {
            end = CACurrentMediaTime()
            print("actor-测量时间:\(end - start)")
        }
    }
}
Actor模式
传统线程的传递模式
actor 还支持异步属性
struct BundleFile {
    let filename: String
    
    var contents: String {
        get async throws {
            guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else {
                throw FileError.missing
            }
    
            do {
                return try String(contentsOf: url)
            } catch {
                throw FileError.unreadable
            }
        }
    }
}

c、Continuation
老业务怎么平滑的过渡到async呢?
Swift 5.5 提供了 Continuations 让我们可以把已有的基于闭包的方法转换成 async 方法。

 let thread1 = Thread {
            Task.init {
                let image = try await AsynsData().processImageData()
                self.imageView2.image = image
            }
        }
        thread1.start()
        
        let thread2 = Thread {
            Task.init {
                let image = try await AsynsData().processImageData()
                self.imageView3.image = image
            }
        }
        thread2.start()

 func processImageData() async throws -> UIImage? {
        let upload1 = try await uploadResource()
        let upload2 = try await uploadResource()
        let upload3 = try await uploadResource()
        let downloadImage = try await downloadImage([upload1.data,
                                                     upload2.data,
                                                     upload3.data])
        return downloadImage
    }

//图片下载
    func downloadImage(_ data:[Data]) async throws -> UIImage {
        await withUnsafeContinuation { continuation in
            AF.download("https://httpbin.org/image/png").responseData { response in
                if let data = response.value, let image = UIImage(data: data) {
                    continuation.resume(returning: image)
                }
            }
        }
    }

    //文件上传
    func uploadResource() async throws -> (error: APISessionError?, data: Data) {
        await withUnsafeContinuation { continuation in
            guard let fileUrl = Bundle.main.url(forResource: "symbold", withExtension: "text") else {
                continuation.resume(returning:(APISessionError.networkError(error: nil, statusCode: 503), Data()) )
                return
            }
            AF.upload(fileUrl, to: "https://httpbin.org/post").response { response in
                if let data = response.data {
                    continuation.resume(returning:(nil, data))
                } else {
                    continuation.resume(returning:(.noData, Data()))
                }
            }

        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,583评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,669评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,684评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,682评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,533评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,396评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,814评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,458评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,745评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,789评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,565评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,410评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,828评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,050评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,342评论 1 253
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,793评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,010评论 2 337

推荐阅读更多精彩内容