常見問題總結(jié):
1: GCD里面有多少種全局隊(duì)列?
詳情請查看dispatch queue.h 頭文件
Global Dispatch Queue有如下8種
Global Dispatch Queue (High priority)
Global Dispatch Queue (Default priority)
Global Dispatch Queue (Low priority)
Global Dispatch Queue (Background priority)
Global Dispatch Queue (High overcommit priority)
Global Dispatch Queue (Default overcommit priority)
Global Dispatch Queue (Low overcommit priority)
Global Dispatch Queue (Background overcommit priority)
注意前面四種 和后面四種不同優(yōu)先級的Queue有一詞之差:Overcommit朽褪。其區(qū)別就在于Overcommit Queue不管系統(tǒng)狀態(tài)如何都會強(qiáng)制生成線程隊(duì)列置吓。
2: 基本概念闡述
1:GCD 是什么鳍贾?
GCD全稱Grand Central Dispatch
GCD 在后端管理著一個(gè)線程池。GCD 不僅決定著你的代碼塊將在哪個(gè)線程被執(zhí)行交洗,它還根據(jù)可用的系統(tǒng)資源對這些線程進(jìn)行管理骑科。這樣可以將開發(fā)者從線程管理的工作中解放出來,通過集中的管理線程构拳,來緩解大量線程被創(chuàng)建的問題咆爽。
GCD 帶來的另一個(gè)重要改變是,作為開發(fā)者可以將工作考慮為一個(gè)隊(duì)列置森,而不是一堆線程斗埂,這種并行的抽象模型更容易掌握和使用。當(dāng)多個(gè)隊(duì)列要處理塊時(shí)凫海,系統(tǒng)可以自由分配額外的線程來同時(shí)調(diào)用塊呛凶。當(dāng)隊(duì)列變空時(shí),這些線程會自動釋放.
首先行贪,系統(tǒng)提供給你一個(gè)叫做 主隊(duì)列(main queue) 的特殊隊(duì)列漾稀。和其它串行隊(duì)列一樣,這個(gè)隊(duì)列中的任務(wù)一次只能執(zhí)行一個(gè)建瘫。然而崭捍,它能保證所有的任務(wù)都在主線程執(zhí)行,而主線程是唯一可用于更新 UI 的線程啰脚。這個(gè)隊(duì)列就是用于發(fā)生消息給 UIView 或發(fā)送通知的殷蛇。
系統(tǒng)同時(shí)提供給你好幾個(gè)并發(fā)隊(duì)列。它們叫做 全局調(diào)度隊(duì)列(Global Dispatch Queues) 橄浓。目前的四個(gè)全局隊(duì)列有著不同的優(yōu)先級:background粒梦、low、default 以及 high荸实。要知道匀们,Apple 的 API 也會使用這些隊(duì)列,所以你添加的任何任務(wù)都不會是這些隊(duì)列中唯一的任務(wù)泪勒。
最后昼蛀,你也可以創(chuàng)建自己的串行隊(duì)列或并發(fā)隊(duì)列。
主隊(duì)列圆存、
串行隊(duì)列叼旋、
并發(fā)隊(duì)列。
2.GCD相比其他多線程有哪些優(yōu)點(diǎn)沦辙?
GCD 能通過推遲昂貴計(jì)算任務(wù)并在后臺運(yùn)行它們來改善你的應(yīng)用的響應(yīng)性能夫植。
GCD 提供一個(gè)易于使用的并發(fā)模型而不僅僅只是鎖和線程,以幫助我們避開并發(fā)陷阱。
GCD 具有在常見模式(例如單例)上用更高性能的原語優(yōu)化你的代碼的潛在能力详民。
GCD 會自動利用更多的CPU內(nèi)核(比如雙核延欠、四核)
3.GCD術(shù)語
串行(Serial):讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
并發(fā)(Concurrent):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效沈跨。
同步(Synchronous):在當(dāng)前線程中執(zhí)行任務(wù)由捎,不具備開啟新線程的能力
異步(Asynchronous):在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
#pragma mark - 串行隊(duì)列
- (void)dispatch_serialQueue{
//串行隊(duì)列
dispatch_queue_t serialQueue;
serialQueue = dispatch_queue_create("com.example.SerialQueue", DISPATCH_QUEUE_SERIAL);// dispatch_queue_attr_t設(shè)置成NULL的時(shí)候默認(rèn)代表串行饿凛。
dispatch_async(serialQueue, ^{
// something
NSLog(@"current thread = com.example.SerialQueue %@", [NSThread currentThread]);
});
}
#pragma mark - 并發(fā)隊(duì)列
- (void)dispatch_concurrentQueue{
//并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue;
concurrentQueue = dispatch_queue_create("com.example.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
NSLog(@"current thread = com.example.ConcurrentQueue %@", [NSThread currentThread]);
});
}
#pragma mark - 主隊(duì)列
- (void)dispath_mainQueue{
// 主隊(duì)列:
dispatch_queue_t mainQueue;
mainQueue = dispatch_get_main_queue();
dispatch_async(dispatch_get_main_queue(), ^{
// something
NSLog(@"current thread = %@ main ", [NSThread currentThread]);
});
}
#pragma mark - 自定義dispatch_queue_t
- (void)dispatch_queue_t{
// 自定義dispatch_queue_t
dispatch_queue_t emailQueue = dispatch_queue_create("www.summerHearts@163.com", NULL);
dispatch_async(emailQueue, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
});
}
#pragma mark -信號量
- (void)dispatch_semaphore{
//信號量:
/*
dispatch Semaphore 是持有計(jì)數(shù)的信號狞玛,該計(jì)數(shù)是多線程編程中的計(jì)數(shù)類型信號。所謂信號涧窒,就是過馬路時(shí)常用的手旗心肪。可以通過時(shí)舉起手旗纠吴,不可通過時(shí)放下手旗硬鞍。使用計(jì)數(shù)來實(shí)現(xiàn)該功能。計(jì)數(shù)為0時(shí)等待戴已,計(jì)數(shù)為1或者大于1固该,減去1而不等待。
*/
dispatch_queue_t queues = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 10; ++i) {
dispatch_async(queues, ^{
/*
* 等待 Dispatch Semaphore.
* 一直等待恭陡,直到Dispatch Semaphore的計(jì)數(shù)達(dá)到大于等于1
* // 由于是異步執(zhí)行的蹬音,所以每次循環(huán)Block里面的dispatch_semaphore_signal根本還沒有執(zhí)行就會執(zhí)行dispatch_semaphore_wait上煤,從而semaphore-1.當(dāng)循環(huán)10此后休玩,semaphore等于0,則會阻塞線程劫狠,直到執(zhí)行了Block的dispatch_semaphore_signal 才會繼續(xù)執(zhí)行
*/
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@">>>> %ld",result);
/*
此時(shí) result為0拴疤, 可安全的執(zhí)行需要進(jìn)行排他控制的處理。該處理結(jié)束的時(shí)候通過dispatch_semaphore_signal函數(shù)將dispatch_semaphore的計(jì)數(shù)值加1.
*/
/*
由于Dispatch semaphore 的計(jì)數(shù)值達(dá)到大于或者等于等一1,所以將 Dispatch semaphore的計(jì)數(shù)值減去1. dispatch_semaphore_wait函數(shù)執(zhí)行返回独泞。即執(zhí)行到此呐矾,Dispatch semaphore 的計(jì)數(shù)值為0.
*/
[array addObject:[NSNumber numberWithInt:i]];
/*
dispatch_semaphore_signal函數(shù)將dispatch semaphore 的計(jì)數(shù)值加1.如果有通過 dispatch_semaphore_wait函數(shù)等待 dispatch semaphore的計(jì)數(shù)值增加的線程就由最先等待的線程執(zhí)行驹饺。
*/
dispatch_semaphore_signal(semaphore);
});
//dispatch_semaphore_wait 函數(shù)等待Dispatch Semaphore的計(jì)數(shù)值達(dá)到大于或者等于1.當(dāng)計(jì)數(shù)值大于等于1宵统,或者在待機(jī)中技術(shù)值大于或者等于1,對該計(jì)數(shù)進(jìn)行減法并從dispatch_semaphore_wait函數(shù)返回硫戈。
}
}
#pragma mark - 柵欄
- (void)dispatch_barrier_async{
// 自定義并發(fā)隊(duì)列
/*
通過dispatch_barrier_async函數(shù)提交的任務(wù)會等它前面的任務(wù)執(zhí)行完才開始荞膘,然后它后面的任務(wù)必須等它執(zhí)行完畢才能開始.
必須使用dispatch_queue_create創(chuàng)建的隊(duì)列才會達(dá)到上面的效果.dispatch_barrier_async 起到了“承上啟下”的作用罚随。它保證此前的任務(wù)都先于自己執(zhí)行,此后的任務(wù)也遲于自己執(zhí)行羽资。正如barrier的含義一樣淘菩,它起到了一個(gè)柵欄、或是分水嶺的作用屠升。
這樣一來潮改,使用并行隊(duì)列和 dispatc_barrier_async 方法狭郑,就可以高效的進(jìn)行數(shù)據(jù)和文件讀寫了。
*/
/*
dispatch_barrier_async函數(shù)會等待追加到concurrent dispatch queue 上的并行執(zhí)行的處理全部結(jié)束之后汇在,再將制訂的處理追加到該concurrent dispatch queue中翰萨。然后再由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢之后,concurrent dispatch queue才恢復(fù)過來一般的動作糕殉,追加到該concurrent dispatch queue的處理又開始并行執(zhí)行缨历。
*/
// 自定義串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^() {
NSLog(@"dispatch_async--1");
});
dispatch_async(serialQueue, ^() {
NSLog(@"dispatch_async--2");
});
dispatch_barrier_async(serialQueue, ^() {
NSLog(@"dispatch-barrier_async");
sleep(1);
});
// 雖然異步, 但是在串行隊(duì)列中, 會按照順序執(zhí)行
dispatch_async(serialQueue, ^() {
NSLog(@"dispatch_async--3");
sleep(1);
});
dispatch_async(serialQueue, ^() {
NSLog(@"dispatch_async--4");
});
}
#pragma mark - 一次操作
- (void)dispatch_once_t{
// 一次性執(zhí)行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
}
#pragma mark -延時(shí)執(zhí)行操作
- (void)dispatch_queue_after{
// 延遲2秒執(zhí)行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
}
#pragma mark - 線程組
- (void)dispatch_queue_group{
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行執(zhí)行的線程一
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
// 并行執(zhí)行的線程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
});
}
需要注意的點(diǎn)
#define LOCK(...) dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(self->_lock);
如何使用
dispatch_queue_t concurrentQueue = dispatch_queue_create("www.fangchuang.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
_lock = dispatch_semaphore_create(1);
for (int i = 0; i < 50; i++) {
//并列隊(duì)列的異步執(zhí)行
dispatch_group_async(group, concurrentQueue, ^{
//如果lock的值大于等于1繼續(xù)執(zhí)行,否則(-1)返回
LOCK(
NSLog(@">>>> %d",i);
);
});
}
如何給一個(gè)線程設(shè)置標(biāo)識符
dispatch_queue_t MessageDataPrepareQueue(){
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("DataPrepareSpecificKeyMessage.queue", 0);
dispatch_queue_set_specific(queue, DispatchMessageDataPrepareSpecificKey, (void *)DispatchMessageDataPrepareSpecificKey, NULL);
});
return queue;
}
UITableView滑動時(shí)并發(fā)其它操作需要注意點(diǎn)
dispatch_sync(MessageDataPrepareQueue(), ^{
if (dispatch_get_specific(DispatchMessageDataPrepareSpecificKey)) {
//當(dāng)前隊(duì)列是queue1隊(duì)列糙麦,所以能取到DispatchMessageDataPrepareSpecificKey對應(yīng)的值辛孵,故而執(zhí)行
//后臺線程處理寬度計(jì)算,處理完之后同步拋到主線程插入
}else{
}
});
//此時(shí)遇到tableView 正在滑動就延時(shí)操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), MessageDataPrepareQueue(), ^{
NSLog(@"延時(shí)操作");
});
@synchronized需要注意點(diǎn)
濫用 @synchronized (self) 會很危險(xiǎn)赡磅,因?yàn)樗型綁K都會彼此搶奪同一個(gè)鎖魄缚。要是有很多屬性都這樣寫,那么每個(gè)屬性的同步塊都要等待其他所有同步塊執(zhí)行完畢才能執(zhí)行焚廊,其實(shí)我們只是想要每個(gè)屬性各自獨(dú)立的同步
實(shí)用@synchronized (self)從某種程度上來說冶匹,是線程安全的,但卻無法保證訪問該對象時(shí)是線程安全的咆瘟。當(dāng)然嚼隘,訪問屬性的操作確實(shí)是原子的。實(shí)用屬性時(shí)袒餐,確實(shí)能從中獲取有效值飞蛹,然而在同一個(gè)線程上多次調(diào)用getter方法,每次獲取的結(jié)果卻是未必相同的灸眼。在兩次訪問操作之間卧檐,可能有其他線程寫入了新的值。
解決方案: 將寫入操作和讀取操作放在同一個(gè)線程中執(zhí)行焰宣,保證數(shù)據(jù)同步霉囚。
dispatch_barrier在并發(fā)隊(duì)列中創(chuàng)建一個(gè)同步點(diǎn),當(dāng)并發(fā)隊(duì)列中遇到一個(gè) dispatch_barrier時(shí)匕积,會延時(shí)執(zhí)行該 dispatch_barrier盈罐,等待在 dispatch_barrier之前提交的任務(wù)block執(zhí)行完后才開始執(zhí)行,之后闪唆,并發(fā)隊(duì)列繼續(xù)執(zhí)行后續(xù)block任務(wù)盅粪。
在隊(duì)列中,柵欄塊必須單獨(dú)執(zhí)行苞氮,不能和其他塊并行湾揽。并發(fā)隊(duì)列如果發(fā)現(xiàn)接下來要處理的塊是個(gè)柵欄塊,那么就一直等待當(dāng)前所有并發(fā)塊都執(zhí)行完畢,才會單獨(dú)執(zhí)行這個(gè)柵欄塊库物。等待柵欄塊執(zhí)行過后霸旗,再按正常方式繼續(xù)向下執(zhí)行。
下邊給出案例:銀行賬戶存取案例的優(yōu)化戚揭,保證真正的線程安全
#import <Foundation/Foundation.h>
typedef void(^AccountBalanceBock)();
@interface Account : NSObject
@property (nonatomic ,assign) NSInteger balance;
@property (copy ,nonatomic) AccountBalanceBock accountBalanceBock;
- (void)withdraw:(NSInteger)amount success:(AccountBalanceBock)success;
- (void)deposit:(NSInteger)amount success:(AccountBalanceBock)success;
@end
#import "Account.h"
@interface Account ()
@property (nonatomic ,strong) dispatch_queue_t queue;
@property (nonatomic ,assign) NSInteger privateBalance;
@end
@implementation Account
- (Account *)init{
self = [super init];
if (self) {
self.queue = dispatch_queue_create("www.fangchang.com", NULL);
self.privateBalance = 0;
}
return self;
}
- (NSInteger)balance{
dispatch_sync(self.queue, ^{
_balance = self.privateBalance;
});
// 將寫入操作和讀取操作放在同一個(gè)線程中執(zhí)行诱告,保證數(shù)據(jù)同步。
return _balance;
}
- (void)withdraw:(NSInteger)amount success:(AccountBalanceBock)success{
NSInteger newBalance = self.privateBalance - amount;
if (newBalance<0) {
NSLog(@"當(dāng)前賬戶余額不足100");
return ;
}
sleep(1);
self.privateBalance = newBalance;
dispatch_async(dispatch_get_main_queue(), ^{
if (success!=nil) {
success();
}
});
}
- (void)deposit:(NSInteger)amount success:(AccountBalanceBock)success{
NSInteger newBalance = self.privateBalance + amount;
self.privateBalance = newBalance;
dispatch_async(dispatch_get_main_queue(), ^{
if (success!=nil) {
success();
}
});
}
- (void)setPrivateBalance:(NSInteger)privateBalance{
dispatch_barrier_async(self.queue, ^{
_privateBalance = privateBalance;
});
// dispatch_barrier在并發(fā)隊(duì)列中創(chuàng)建一個(gè)同步點(diǎn)民晒,當(dāng)并發(fā)隊(duì)列中遇到一個(gè) dispatch_barrier時(shí)精居,會延時(shí)執(zhí)行該 dispatch_barrier,等待在 dispatch_barrier之前提交的任務(wù)block執(zhí)行完后才開始執(zhí)行潜必,之后靴姿,并發(fā)隊(duì)列繼續(xù)執(zhí)行后續(xù)block任務(wù)
}
@end
github 地址:
https://github.com/summerHearts/ThreadSanitizer
https://github.com/summerHearts/iOSTips