关于多线程之GCD的一些学习要点
GCD是當前多線程使用最方便的,也是使用比較多的。
學習GCD主要集中在一下幾點:
一、隊列,同步,異步
1.主隊列:dispatch_get_main_queue();
2.串行隊列:dispatch_queue_create("queue", 0);
3.并行隊列:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4.同步,異步就不需要再多說什么。
?
對上面關鍵詞的一些解釋:
主隊列:主隊列只能使用異步來執行隊列中的任務,使用同步的話會造成死循環。同時執行任務的時候是在主線程中執行的。
串行隊列:(1)、添加到串行隊列中的任務是一個接著一個執行的,也就是當上一個任務執行完之后才開始下一個任務;
??????????????????????? 但是可以創建多個串行隊列,每個串行隊列之間是相互獨立的,可以并發執行。
???????????? ?(2)、如果用同步執行的話就是在當前線程上執行;
????????????? (3)、如果用異步執行的話就是新開辟一個線程執行。
并行隊列:(1)、添加到并行隊列中的任務可以并發啟動,啟動順序仍然是一個接著一個,但是后者可以不等前者執行完就可以開始執行。
??????????????????????? 并行隊列也可以創建多個,各個之間也是相互獨立,并發執行的。
???????????? ?(2)、如果用同步執行的話就是在當前線程上執行;
????????????? (3)、如果用異步執行的話就是新開辟一個線程執行。
????????????
二、經常用到的地方
1.任務執行完畢之后再進行一些操作
2.只執行一次
3.延時操作
?
對上面用處的一些解釋:
(1)、任務執行完畢之后再進行一些操作,如果是一些簡單的操作可以直接使用串行隊列實現。但是一些比較費時的操作就需要用到隊列組了。
??? //1.創建一個隊列組
??????? dispatch_group_t group = dispatch_group_create();
????
??? //2.開啟一個任務1
??? dispatch_group_async(group, global_quque, ^{
?
??? });
????
??? //3.開啟一個任務2
??? dispatch_group_async(group, global_quque, ^{
?????? });
????
?? //4.等group中的所有任務都執行完畢, 再回到主線程執行其他操作
??? dispatch_group_notify(group,main_queue, ^{
???? });
????
?
(2)、只執行一次
????????? static dispatch_once_t onceToken;
????????? dispatch_once(&onceToken, ^{
????? ??? // 只執行1次的代碼(這里面默認是線程安全的)
});
?
(3)、延時操作
????????? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
??????? ? //延遲執行的方法
??? });
?
(4)、暫停和恢復隊列中的任務
dispatch_suspend和dispatch_resume
我們知道NSOperationQueue有暫停(suspend)和恢復(resume)。其實GCD中的隊列也有類似的功能。用法也非常簡單:
dispatch_suspend(queue)?//暫停某個隊列 ?
dispatch_resume(queue)??//恢復某個隊列 ?
這些函數不會影響到隊列中已經執行的任務,隊列暫停后,已經添加到隊列中但還沒有執行的任務不會執行,直到隊列被恢復。
?
(5)、承上啟下
dispatch_async(queue, block1_for_reading) ?
dispatch_async(queue, block2_for_reading)
?
dispatch_barrier_async(queue, block_for_writing)
?
dispatch_async(queue, block3_for_reading) ?
dispatch_async(queue, block4_for_reading) ?
dispatch_barrier_async?會把并行隊列的運行周期分為這三個過程:
總的來說,dispatch_barrier_async?起到了“承上啟下”的作用。它保證此前的任務都先于自己執行,此后的任務也遲于自己執行。正如barrier的含義一樣,它起到了一個柵欄、或是分水嶺的作用。
這樣一來,使用并行隊列和?dispatc_barrier_async?方法,就可以高效的進行數據和文件讀寫了。
?
(6)、信號量
dispatch_semaphore
首先介紹一下信號量(semaphore)的概念。信號量是持有計數的信號,不過這么解釋等于沒解釋。我們舉個生活中的例子來看看。
假設有一個房子,它對應進程的概念,房子里的人就對應著線程。一個進程可以包括多個線程。這個房子(進程)有很多資源,比如花園、客廳等,是所有人(線程)共享的。
但是有些地方,比如臥室,最多只有兩個人能進去睡覺。怎么辦呢,在臥室門口掛上兩把鑰匙。進去的人(線程)拿著鑰匙進去,沒有鑰匙就不能進去,出來的時候把鑰匙放回門口。
這時候,門口的鑰匙數量就稱為信號量(Semaphore)。很明顯,信號量為0時需要等待,信號量不為零時,減去1而且不等待。
在GCD中,創建信號量的語法如下:
var?semaphore = dispatch_semaphore_create(2) ?
這句代碼通過?dispatch_semaphore_create?方法創建一個信號量并設置初始值為 2。然后就可以調用?dispatch_semaphore_wait?方法了。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) ?
dispatch_semaphore_wait?方法表示一直等待直到信號量的值大于等于 1,當這個方法執行后,會把第一個信號量參數的值減 1。
第二個參數是一個?dispatch_time_t?類型的時間,它表示這個方法最大的等待時間。這在第一章中已經講過,比如?DISPATCH_TIME_FOREVER?表示永久等待。
返回值也和?dispatch_group_wait?方法一樣,返回 0 表示在規定的等待時間內第一個參數信號量的值已經大于等于 1,否則表示已超過規定等待時間,但信號量的值還是 0。
dispatch_semaphore_wait?方法返回 0,因為此時的信號量的值大于等于一,任務獲得了可以執行的權限。這時候我們就可以安全的執行需要進行排他控制的任務了。
任務結束時還需要調用?dispatch_semaphore_signal()?方法,將信號量的值加 1。這類似于之前所說的,從臥室出來要把鎖放回門上,否則后來的人就無法進入了。
我們來看一個完整的例子:
var?semaphore = dispatch_semaphore_create(1) ?
let?queue = dispatch_queue_create("com.gcd.kt", DISPATCH_QUEUE_CONCURRENT) ?
var?array: [Int] = []
?
for?i?in?1...100000?{ ?
? ? dispatch_async(queue, { () -> Void?in
? ? ? ??/*
? ? ? ? ? ? 某個線程執行到這里,如果信號量值為1,那么wait方法返回1,開始執行接下來的操作。
? ? ? ? ? ? 與此同時,因為信號量變為0,其它執行到這里的線程都必須等待
? ? ? ? */
? ? ? ? dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
?
? ? ? ??/*
? ? ? ? ? ? 執行了wait方法后,信號量的值變成了0。可以進行接下來的操作。
? ? ? ? ? ? 這時候其它線程都得等待wait方法返回。
? ? ? ? ? ? 可以對array修改的線程在任意時刻都只有一個,可以安全的修改array
? ? ? ? */
? ? ? ? array.append(i)
?
? ? ? ??/*
? ? ? ? ? ? 排他操作執行結束,記得要調用signal方法,把信號量的值加1。
? ? ? ? ? ? 這樣,如果有別的線程在等待wait函數返回,就由最先等待的線程執行。
? ? ? ? */
? ? ? ? dispatch_semaphore_signal(semaphore)
? ? })
}
?
總結:
1、常用的延時操作:
??? (1)????dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
??????? ? //延遲執行的方法
??? });
??? (2) ? [self performSelector:@selector(test) withObject:nil afterDelay:1.0];
2、回到主線程的操作:
??? (1)??? dispatch_async(dispatch_get_main_queue(), ^{
?????????????? // 回到主線程,執?UI刷新操作 });
??? (2)??? [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
?
轉載于:https://www.cnblogs.com/danMing-love/p/5598361.html
總結
以上是生活随笔為你收集整理的关于多线程之GCD的一些学习要点的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 哈理工OJ—1598【DP最长公共子序列
 - 下一篇: 类的设计