偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Swift 并發(fā)中的 Yielding 和 Debouncing

開發(fā) 前端
本篇文章的主題是任務(wù)讓步(Task Yielding) 和 防抖(Debouncing)。Swift 并發(fā)為我們提供了兩個(gè)簡(jiǎn)單但非常強(qiáng)大的函數(shù):yield? 和 sleep。今天我們就來看看它們的用法,以及在什么場(chǎng)景下應(yīng)該使用它們。

前言 

本篇文章的主題是 任務(wù)讓步(Task Yielding) 和 防抖(Debouncing)。Swift 并發(fā)為我們提供了兩個(gè)簡(jiǎn)單但非常強(qiáng)大的函數(shù):yield 和 sleep。今天我們就來看看它們的用法,以及在什么場(chǎng)景下應(yīng)該使用它們。

什么是任務(wù)防抖(Debouncing)? 

想象一下,你正在開發(fā)一個(gè)搜索功能,用戶每輸入一個(gè)字符,程序就會(huì)去一個(gè)龐大的數(shù)據(jù)集里查找匹配的結(jié)果。如果不加控制,每次鍵入都會(huì)觸發(fā)新的搜索任務(wù),可能會(huì)導(dǎo)致多個(gè)任務(wù)同時(shí)執(zhí)行,影響性能甚至引發(fā)競(jìng)爭(zhēng)條件(Race Condition)。

比如,下面這個(gè) SwiftUI 代碼:

@MainActor @Observablefinalclass Store {
    private(set) var results: [HKCorrelation] = []
    privatelet store = HKHealthStore()

    func search(matching query: String) async {
        // 執(zhí)行復(fù)雜的搜索任務(wù)
    }
}

struct ContentView: View {
    @Stateprivatevar store = Store()
    @Stateprivatevar query = ""

    var body: some View {
        NavigationStack {
            List(store.results, id: \.uuid) { result in
                Text(verbatim: result.endDate.formatted())
            }
            .searchable(text: $query)
            .task(id: query) {
                await store.search(matching: query)
            }
        }
    }
}

在這個(gè)代碼里,每次用戶輸入新字符,都會(huì)創(chuàng)建一個(gè)新的任務(wù)去執(zhí)行搜索。而 Swift 并發(fā)采用 協(xié)作式取消機(jī)制(Cooperative Cancellation),也就是說,它不會(huì)直接強(qiáng)行終止任務(wù),而是提供一個(gè)“取消標(biāo)記”,任務(wù)需要自己檢查并響應(yīng)取消請(qǐng)求。因此,這種寫法可能會(huì)導(dǎo)致多個(gè)搜索任務(wù)并行運(yùn)行,消耗不必要的計(jì)算資源。

為了解決這個(gè)問題,我們可以用 防抖(Debouncing) 技術(shù)。

如何用 sleep 實(shí)現(xiàn)防抖? 

防抖的思路很簡(jiǎn)單:

  • 戶輸入時(shí),我們 等待一小段時(shí)間,看看用戶是否繼續(xù)輸入。
  • 如果輸入仍然在變化,我們就 繼續(xù)等待,而不是立即啟動(dòng)搜索。
  • 只有當(dāng)輸入 穩(wěn)定 一定時(shí)間后,才觸發(fā)搜索任務(wù)。

換句話說,如果用戶輸入 "apple",我們希望忽略 "a", "ap", "app", "appl",只在最終輸入 "apple" 后再進(jìn)行搜索。

在 Swift 并發(fā)中,我們可以用 Task.sleep 來實(shí)現(xiàn)這個(gè)效果:

struct ContentView: View {
    @Stateprivatevar store = Store()
    @Stateprivatevar query = ""

    var body: some View {
        NavigationStack {
            List(store.results, id: \.uuid) { result in
                Text(verbatim: result.endDate.formatted())
            }
            .searchable(text: $query)
            .task(id: query) {
                do {
                    try await Task.sleep(for: .seconds(1))  // 等待 1 秒
                    await store.search(matching: query)  // 執(zhí)行搜索
                } catch {
                    // 任務(wù)可能因?yàn)樾螺斎氡蝗∠?                }
            }
        }
    }
}

為什么這樣就能實(shí)現(xiàn)防抖?

  • Task.sleep(for: .seconds(1)) 讓當(dāng)前任務(wù)暫停 1 秒。
  • 如果用戶在 1 秒內(nèi)繼續(xù)輸入,之前的任務(wù)會(huì)被取消,新任務(wù)重新計(jì)時(shí)。
  • 只有 用戶停止輸入超過 1 秒,才會(huì)觸發(fā)真正的搜索任務(wù)。

效果: 這樣可以避免在輸入過程中反復(fù)觸發(fā)搜索,減少不必要的計(jì)算量。

什么是任務(wù)讓步(Task Yielding)? 

除了防抖,Swift 并發(fā)還提供了 任務(wù)讓步(Task Yielding),讓你在執(zhí)行長(zhǎng)時(shí)間任務(wù)時(shí),主動(dòng)把線程讓出來,讓其他任務(wù)有機(jī)會(huì)運(yùn)行。

想象一個(gè)場(chǎng)景:

你需要解析一批巨大的 JSON 文件,并將數(shù)據(jù)保存到磁盤。這個(gè)過程可能會(huì)運(yùn)行很久,占用線程資源。如果你在主線程或并發(fā)線程池(Cooperative Thread Pool)上運(yùn)行這種任務(wù),會(huì) 阻塞其他任務(wù)的執(zhí)行,導(dǎo)致性能問題。

比如,下面是一個(gè)解析 JSON 文件的代碼:

struct Item: Decodable {
    // 解析 JSON 的結(jié)構(gòu)
}

struct DataHandler {
    func process(json files: [Data]) async throws -> [Item] {
        let decoder = JSONDecoder()
        var result: [Item] = []

        for file in files {
            let items = try decoder.decode([Item].self, from: file)
            result.append(contentsOf: items)
        }

        return result
    }
}

這個(gè) process 函數(shù)會(huì)遍歷所有 JSON 文件,并解析它們。但問題是:

  • 解析 JSON 是一個(gè) 同步操作,它不會(huì)自動(dòng)釋放 CPU 資源。
  • 如果 JSON 文件很大,整個(gè)解析過程可能會(huì) 占用線程很長(zhǎng)時(shí)間,導(dǎo)致其他任務(wù)被阻塞。

如何用 yield 讓出線程? 

為了解決這個(gè)問題,我們可以 在每次解析完一個(gè) JSON 文件后,讓出線程,讓其他任務(wù)有機(jī)會(huì)執(zhí)行:

struct DataHandler {
    func process(json files: [Data]) async throws -> [Item] {
        let decoder = JSONDecoder()
        var result: [Item] = []

        for file in files {
            let items = try decoder.decode([Item].self, from: file)
            result.append(contentsOf: items)

            await Task.yield()  // 讓出線程,讓其他任務(wù)有機(jī)會(huì)執(zhí)行
        }

        return result
    }
}

任務(wù)讓步的好處:

  • await Task.yield() 會(huì)讓當(dāng)前任務(wù) 暫停一下,讓其他等待中的任務(wù)有機(jī)會(huì)執(zhí)行。
  • 之后,系統(tǒng)會(huì)恢復(fù)這個(gè)任務(wù)的執(zhí)行,繼續(xù)處理下一個(gè) JSON 文件。
  • 這樣可以 更公平地分配 CPU 資源,防止某個(gè)任務(wù)獨(dú)占線程。

什么時(shí)候需要 yield? 

通常來說,如果你的代碼已經(jīng)是 異步的(async/await),系統(tǒng)會(huì)自動(dòng)在 await 語句處讓出線程。所以 **大部分情況下,你不需要手動(dòng) yield**。

但是,當(dāng)你處理 非異步 API(比如 JSON 解析、圖片處理、大量計(jì)算等)時(shí),手動(dòng) yield 可能會(huì)提升性能。

總結(jié) 

  • 防抖(Debouncing)

a.適用于 用戶頻繁輸入的場(chǎng)景,如搜索框、按鈕點(diǎn)擊等。

b.通過 Task.sleep(for:) 實(shí)現(xiàn),等輸入穩(wěn)定后再執(zhí)行任務(wù)。

c.避免頻繁創(chuàng)建任務(wù),提高性能。

  • 任務(wù)讓步(Task Yielding)

      a.適用于 長(zhǎng)時(shí)間運(yùn)行的計(jì)算密集型任務(wù),如解析 JSON、圖片處理等。

      b.通過 Task.yield() 讓出 CPU,避免線程被長(zhǎng)時(shí)間占用。

      c.讓其他任務(wù)有機(jī)會(huì)執(zhí)行,提高系統(tǒng)響應(yīng)速度。

這兩個(gè)技巧雖然簡(jiǎn)單,但在實(shí)際開發(fā)中非常有用,可以幫助你更高效地利用 Swift 并發(fā),讓你的應(yīng)用運(yùn)行得更流暢!

責(zé)任編輯:姜華 來源: Swift社區(qū)
相關(guān)推薦

2025-03-21 09:01:34

Swift任務(wù)取消機(jī)制協(xié)作式取消

2022-04-06 09:10:03

抽象類型普通類型Swift

2023-11-03 11:56:34

2015-01-21 16:25:29

Swift指針

2015-03-16 10:33:14

Swift指針

2015-07-08 16:43:02

Configurati

2015-11-23 10:07:19

Swift模式匹配

2021-11-10 15:18:16

JavaGo命令

2022-04-26 08:41:38

Swift并發(fā)系統(tǒng)iOS

2023-11-20 22:44:09

Golang并發(fā)

2021-12-22 15:13:03

iOS 15Swift二進(jìn)制

2022-05-11 09:01:54

Swift類型系統(tǒng)幻象類型

2022-07-04 08:54:39

Swift處理器項(xiàng)目

2020-11-12 19:15:54

Swift蘋果開發(fā)

2021-07-07 11:41:38

Swift key paths

2022-05-25 09:15:01

Swift 5.6占位符

2022-06-13 09:02:06

Swift類型占位符

2025-05-22 09:01:28

2023-03-27 09:57:00

PostgreSQL并發(fā)索引

2024-01-15 08:39:12

ArraysSetsTypeScript
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)