「iOS」——GCD其他方法詳解

GCD學習

  • GCD其他方法
      • dispatch_semaphore (信號量)
      • **什么是信號量**
      • dispatch_semaphore主要作用
        • dispatch_semaphore主要作用
          • 異步轉同步
          • 設置一個最大開辟的線程數
          • 加鎖機制
        • dispatch_time_t 兩種形式
        • GCD一次性代碼(只執行一次)
      • dispatch_barrier_async/sync柵欄方法


GCD其他方法

前面對GCD進行了簡單的學習,這里筆者對一些容易遺忘和重要的方法再次學習。

dispatch_semaphore (信號量)

什么是信號量

引用學長的解釋

以一個停車場的運作為例。簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看 門人允許其中三輛直接進入,然后放下車攔,剩下的車則必須在入口等待,此后來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知后,打開 車攔,放入外面的一輛進去,如果又離開兩輛,則又可以放入兩輛,如此往復。在這個停車場系統中,車位是公共資源,每輛車好比一個線程,看門人起的就是信號量的作用。

信號量主要用作同步鎖,用于控制GCD最大并發數

主要涉及以下三個函數:

// 創建信號量,value:初始信號量數 如果小于0則會返回NULL
dispatch_semaphore_create(long value); // 發送信號量是的信號量+1
dispatch_semaphore_signal(dispatch_semaphore_t deem);//可以使總信號量減 1,信號總量小于 0 時就會一直等待(阻塞所在線程),否則就可以正常執行。
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); 

注意:信號量的使用前提是:想清楚你需要處理哪個線程等待(阻塞),又要哪個線程繼續執行,然后使用信號量。

dispatch_semaphore主要作用

dispatch_semaphore主要作用
  • 保持線程同步,將異步執行任務轉換為同步執行任務
  • 保證線程安全,為線程加鎖
異步轉同步
- (void)cjl_testSemaphore1{dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//創建異步隊列dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{sleep(1);NSLog(@"執行任務A");//信號量+1dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);dispatch_async(queue, ^{sleep(1);NSLog(@"執行任務B");//信號量+1,相當于解鎖dispatch_semaphore_signal(semaphore);});//當當前的信號量值為0時候會阻塞線,如果大于0的話,信號量-1,不阻塞線程dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);dispatch_async(queue, ^{sleep(1);NSLog(@"執行任務C");//信號量+1,相當于解鎖dispatch_semaphore_signal(semaphore);});}

多次運行的結果都是A,B,C順序執行,讓A,B,C異步執行變成同步執行,dispatch_semaphore相當于加鎖效果

設置一個最大開辟的線程數
- (void)cjl_testSemaphore{
//設置最大開辟的線程數為3dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);//創建一個并發隊列dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//開啟的是10個異步的操作,通過信號量,讓10個異步的最多3個m,剩下的同步等待for (NSInteger i = 0; i < 10; i++) {dispatch_async(queue, ^{//當信號量為0時候,阻塞當前線程dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);NSLog(@"執行任務 %ld", i);sleep(2);NSLog(@"完成當前任務 %ld", i);//釋放信號dispatch_semaphore_signal(semaphore);});}
}

請添加圖片描述

但如果sleep時間變成1,則會出現以下結果:
請添加圖片描述

這是因為出現了優先級反轉的問題。

先介紹什么是優先級反轉。

在程序執行時多個任務可能會對同—份數據產生 競爭’因此任務會使用鎖來保護共享數據°假設現在有3個任務A、B`C’它們的優先 級為A>B>C任務C在運行時持有一把鎖’然后它被高優先級的任務A搶占了(任 務C的鎖沒有被釋放)。此時任務A恰巧也想申請任務C持有的鎖’但是申請失敗,因 此進人阻塞狀態等待任務C放鎖。此時’任務B、C都處于可以運行的狀態,由于任務 B的優先級高于C,因此B優先運行°綜合觀察該情況’就會發現任務B好像優先級高 于任務A’先于任務A執行。

請添加圖片描述

信號量調度是公平隊列(FIFO/不考慮 QoS)+ 系統線程調度是根據優先級(QoS)來的,兩者機制不一致,所以可能造成高優先級線程因為信號量資源被低優先級線程占用而“被阻塞”。

加鎖機制
  • 當你的信號設置為1的時候就相當于加鎖

- (void)viewDidLoad {[super viewDidLoad];NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當前線程NSLog(@"semaphore---begin");semaphoreLock = dispatch_semaphore_create(1);self.ticketCount = 50;// queue1 代表北京火車票售賣窗口dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);// queue2 代表上海火車票售賣窗口dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);__weak typeof(self) weakSelf = self;dispatch_async(queue1, ^{[weakSelf saleTicketSafe];});dispatch_async(queue2, ^{[weakSelf saleTicketSafe];});
}- (void)saleTicketSafe {while (1) {// 相當于加鎖dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);if (self.ticketCount > 0) {  //如果還有票,繼續售賣self.ticketSurplusCount--;NSLog(@"%@", [NSString stringWithFormat:@"剩余票數:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else { //如果已賣完,關閉售票窗口NSLog(@"所有火車票均已售完"); // 相當于解鎖dispatch_semaphore_signal(semaphoreLock);break;} // 相當于解鎖dispatch_semaphore_signal(semaphoreLock);}    
}
dispatch_time_t 兩種形式
  1. 基于dispatch_time函數,表示從當前時間開始的一個時間點
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

使用:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{NSLog(@"dispatch_time");
});

時間單位:

#define NSEC_PER_SEC 1000000000ull 1
#define NSEC_PER_MSEC 1000000ull 1毫秒 
#define USEC_PER_SEC 1000000ull 1
#define NSEC_PER_USEC 1000ull 1微秒
時間點:
#define DISPATCH_TIME_NOW (0ull) 0->現在
#define DISPATCH_TIME_FOREVER (~0ull) -1->永遠
dispatch_time_t定義
typedef uint64_t dispatch_time_t; unsigned long long 64位無符號長整形
GCD一次性代碼(只執行一次)

使用dispatch_once方法能保證某段代碼在程序運行過程中只執被執行1次,即使在多線程的環境下,該方法也可以保證建成安全。之前在創建單例模式時就接觸過該方法。

- (void)once {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 只執行 1 次的代碼(這里面默認是線程安全的)});}

dispatch_barrier_async/sync柵欄方法

在訪問數據庫或者文件的時候,我們可以使用Serial Dispatch Queue可避免數據競爭問題。即多讀單寫

- (void)cjl_testBarrier{/*dispatch_barrier_sync & dispatch_barrier_async應用場景:同步鎖等柵欄前追加到隊列中的任務執行完畢后,再將柵欄后的任務追加到隊列中。簡而言之,就是先執行柵欄前任務,再執行柵欄任務,最后執行柵欄后任務- dispatch_barrier_async:前面的任務執行完畢才會來到這里- dispatch_barrier_sync:作用相同,但是這個會堵塞線程,影響后面的任務執行- dispatch_barrier_async可以控制隊列中任務的執行順序,- 而dispatch_barrier_sync不僅阻塞了隊列的執行,也阻塞了線程的執行(盡量少用)*/[self cjl_testBarrier1];[self cjl_testBarrier2];
}
- (void)cjl_testBarrier1{//串行隊列使用柵欄函數dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL);NSLog(@"開始 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延遲2s的任務1 - %@", [NSThread currentThread]);});NSLog(@"第一次結束 - %@", [NSThread currentThread]);//柵欄函數的作用是將隊列中的任務進行分組,所以我們只要關注任務1、任務2dispatch_barrier_async(queue, ^{NSLog(@"------------柵欄任務------------%@", [NSThread currentThread]);});NSLog(@"柵欄結束 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延遲2s的任務2 - %@", [NSThread currentThread]);});NSLog(@"第二次結束 - %@", [NSThread currentThread]);
}
- (void)cjl_testBarrier2{//并發隊列使用柵欄函數dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);NSLog(@"開始 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延遲2s的任務1 - %@", [NSThread currentThread]);});NSLog(@"第一次結束 - %@", [NSThread currentThread]);//由于并發隊列異步執行任務是亂序執行完畢的,所以使用柵欄函數可以很好的控制隊列內任務執行的順序dispatch_barrier_async(queue, ^{NSLog(@"------------柵欄任務------------%@", [NSThread currentThread]);});NSLog(@"柵欄結束 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延遲2s的任務2 - %@", [NSThread currentThread]);});NSLog(@"第二次結束 - %@", [NSThread currentThread]);
}

重點觀察并發隊列中使用柵欄函數。會在柵欄前的任務完成后,才執行柵欄后的任務。

請添加圖片描述

dispatch_barrier_sync

  • 會阻塞當前線程,看打印結果可知,“start”一定在dispatch_barrier_sync函數前執行,“end”一定在dispatch_barrier_sync函數后執行
  • dispatch_barrier_sync 函數添加的block,在當前線程執行
  • 會將傳入dispatch_barrier_sync函數的線程myQueue中的任務,通過dispatch_barrier_sync函數位置把前后分隔開。

dispatch_barrier_async

  • 不會阻塞當前線程,“start”,“end”與dispatch_barrier_async方法是同步執行的,執行順序不定。
  • dispatch_barrier_sync函數添加的block,在新開的線程執行
  • 會將傳入dispatch_barrier_async函數的線程myQueue中的任務,通過dispatch_barrier_async函數位置把前后分隔開。

這也是為什么柵欄結束會出現在柵欄任務之前。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90954.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90954.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90954.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【圖像處理基石】如何實現一個車輛檢測算法?

基于AI的車牌檢測和識別算法 問題描述、應用場景與難點 問題描述 車牌檢測和識別是計算機視覺領域的一個特定任務&#xff0c;主要包含兩個核心步驟&#xff1a; 車牌檢測&#xff1a;從圖像中準確定位車牌的位置和區域車牌識別&#xff1a;對檢測到的車牌區域進行字符識別&…

計算機學報 2025年 區塊鏈論文 錄用匯總 附pdf下載

計算機學報 Year&#xff1a;2025 2024請看 1 Title: 基于區塊鏈的動態多云多副本數據完整性審計方法研究 Authors: Key words: 區塊鏈&#xff1b;云存儲&#xff1b;多云多副本存儲&#xff1b;數據完整性審計 Abstract: 隨著云計算技術的快速發展和云存儲服務的日益…

計算機網絡-UDP協議

UDP&#xff08;用戶數據報協議&#xff09;是傳輸層的一種無連接、不可靠、輕量級的協議&#xff0c;適用于對實時性要求高、能容忍少量數據丟失的場景&#xff08;如視頻流、DNS查詢等&#xff09;。以下是UDP的詳細解析&#xff1a;1. UDP的核心特點特性說明無連接通信前無需…

子域名收集和c段查詢

子域名收集方法一、sitesite&#xff1a; 要查詢的域名可以查到相關網站二、oneforall &#xff08;子域名查找工具&#xff09;下載后解壓的文件夾在當前文件夾打開終端然后運行命令 python oneforall.py --target xxxxxxxx&#xff08;這里放你要查的網址&#xff09; run最…

計網-TCP擁塞控制

TCP的擁塞控制&#xff08;Congestion Control&#xff09;是核心機制之一&#xff0c;用于動態調整發送方的數據傳輸速率&#xff0c;避免網絡因過載而出現性能急劇下降&#xff08;如丟包、延遲激增&#xff09;。其核心思想是探測網絡可用帶寬&#xff0c;并在擁塞發生時主動…

依賴倒置原則 Dependency Inversion Principle - DIP

基本知識 1.依賴倒置原則&#xff08;DIP&#xff09;是面向對象設計&#xff08;OOD&#xff09;中的五個基本原則之一&#xff0c;通常被稱為 SOLID 原則中的 D 2.核心思想&#xff1a; 高層模塊不應該依賴低層模塊&#xff0c;兩者都應該依賴抽象。 (High-level modules sho…

原生input添加刪除圖標類似vue里面移入顯示刪除[jquery]

<input type"text" id"servicer-search" class"form-control" autocomplete"off" />上面是剛開始的input <div class"servicer-search-box"><input type"text" id"servicer-search" cla…

整理分享 | Photoshop 2025 (v26.5) 安裝記錄

導語&#xff1a; 最近整理資源時&#xff0c;發現有朋友在找新版 Photoshop。正好手邊有 Photoshop 2025年7月的版本&#xff08;v26.5&#xff09;&#xff0c;就記錄下來分享給大家&#xff0c;供有需要的朋友參考。關于這個版本&#xff1a;這個 Photoshop v26.5 安裝包&am…

【Redis】Redis 數據存儲原理和結構

一、Redis 存儲結構 1.1 KV結構 Redis 本質上是一個 Key-Value&#xff08;鍵值對&#xff0c;KV&#xff09;數據庫&#xff0c;在它豐富多樣的數據結構底層&#xff0c;都基于一種統一的鍵值對存儲結構來進行數據的管理和操作 Redis 使用一個全局的哈希表來管理所有的鍵值對…

【RAG優化】深度剖析OCR錯誤,從根源修復RAG應用的識別問題

1. 引言:OCR——RAG系統中的關鍵問題 當我們將一個包含掃描頁面的PDF或一張報告截圖扔給RAG系統時,我們期望它能“讀懂”里面的內容。這個“讀懂”的第一步,就是OCR。然而,OCR過程并非100%準確,它受到圖像質量、文字布局、字體、語言等多種因素的影響。 一個看似微不足道…

【第六節】方法與事件處理器

方法與事件處理器 方法處理器 可以用 v-on 指令監聽 DOM 事件: <div id="example"> <button v-on:click="greet">Greet</button></div>綁定一個單擊事件處理器到一個方法 greet 。下面在 Vue 實例中定義這個方法 var vm=new V…

大語言模型Claude 4簡介

Anthropic公司成立于2021年&#xff0c;由一群OpenAI前員工組成。他們最新發布的大語言模型(Large Language Model, LLM) Claude 4系列包括兩個版本&#xff1a;Claude Opus 4和Claude Sonnet 4&#xff1a;(1).Claude Sonnet 4&#xff1a;是Claude Sonnet 3.7的升級&#xff…

國產化PDF處理控件Spire.PDF教程:Python 將 PDF 轉換為 Markdown (含批量轉換示例)

PDF 是數字文檔管理的普遍格式&#xff0c;但其固定布局特性限制了在需要靈活編輯、更新或現代工作流集成場景下的應用。相比之下&#xff0c;Markdown&#xff08;.md&#xff09;語法輕量、易讀&#xff0c;非常適合網頁發布、文檔編寫和版本控制。 E-iceblue旗下Spire系列產…

PDF轉Markdown - Python 實現方案與代碼

PDF作為廣泛使用的文檔格式&#xff0c;轉換為輕量級標記語言Markdown后&#xff0c;可無縫集成到技術文檔、博客平臺和版本控制系統中&#xff0c;提高內容的可編輯性和可訪問性。本文將詳細介紹如何使用國產Spire.PDF for Python 庫將 PDF 文檔轉換為 Markdown 格式。 技術優…

深度解析 inaSpeechSegmenter:高效音頻語音分割與檢測開源工具

項目簡介 inaSpeechSegmenter 是法國國家視聽研究院(INA)開源的音頻分割與檢測工具,專為廣播、播客、采訪、影視等多媒體內容的自動化處理設計。它能夠高效地將長音頻自動分割為語音、音樂、噪聲、靜音等片段,并支持性別檢測(男聲/女聲),為后續的語音識別、內容檢索、轉…

VirtualBox安裝Ubuntu 22.04后終端無法打開的解決方案

問題現象在VirtualBox中使用"快速安裝"模式安裝Ubuntu 22.04后圖形終端&#xff08;gnome-terminal&#xff09;無法通過圖標或快捷鍵(CtrlAltT)啟動系統其他功能正常根本原因語言環境(Locale)配置異常導致&#xff1a;快速安裝模式可能跳過Locale生成步驟gnome-term…

java磁盤操作與IO流(序列化、Properties類)

目錄 一、磁盤操作 1、File類&#xff1a; &#xff08;1&#xff09;創建File對象&#xff1a; &#xff08;2&#xff09;獲取文件信息&#xff1a; &#xff08;3&#xff09;判斷文件 &#xff08;4&#xff09;刪除文件 &#xff08;5&#xff09;創建文件&#xff…

【WPF】WPF Prism 開發經驗總結:菜單命令刪除項時報 InvalidCastException 的問題分析與解決

WPF Prism 開發經驗總結&#xff1a;菜單命令刪除項時報 InvalidCastException 的問題分析與解決 在 WPF Prism 項目中使用 ContextMenu 執行刪除操作時&#xff0c;遇到一個令人疑惑的問題&#xff1a;命令綁定本身沒有問題&#xff0c;但點擊“刪除”菜單后&#xff0c;程序拋…

《WebGL打造高性能3D粒子特效系統:從0到1的技術探秘》

在游戲里,爆炸時四濺的火花、魔法釋放時閃爍的光暈;在可視化項目中,數據流動時呈現的璀璨光河,這些令人驚嘆的效果,背后離不開強大的技術支撐。而WebGL,作為在瀏覽器端實現硬件加速3D圖形渲染的技術,為我們開啟了構建高性能3D粒子特效系統的大門。 WebGL的渲染管線是整…

全國計算機等級考試二級題庫【C語言】:程序填空題型——結構體 自制答案詳解合輯

二級C語言程序填空題型簡介 1、/**********found**********/緊跟的下面一行的程序設空,一般為3個空; 2、常見錯誤: (1) (2) 3、做題推薦步驟: (1) (2) ---------------一、結構體--------------- 2、題目要求【結構體】 程序通過定義學生結構體變量,存儲了學生…