多線程(GCD)
GCD的基本使用
- GCD是蘋果為多核并行運算提出的解決方案
- GCD會自動利用更多的內(nèi)核
- GCD同NSOperation一樣,也會自動管理線程的生命周期
- GCD是C的代碼,較NSOperation性能更高
- GCD也是以隊列的形式工作,FIFO
- 基本用法
//線程間通信
//進(jìn)入子線程進(jìn)行耗時操作
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
//回主線程進(jìn)行刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",[NSThread currentThread]);
});
});
GCD的兩種隊列
- 并發(fā)隊列(ConcurrentQueue):可以并發(fā)執(zhí)行多個任務(wù),允許開啟多個線程同時執(zhí)行任務(wù)(只是允許,要根據(jù)是否具有開啟新線程的能力),所以只有異步函數(shù)才會對并發(fā)隊列起作用,
- 串行隊列(SerialQueue):讓任務(wù)一個接著一個的執(zhí)行,
- GCD自帶一個特殊的串行隊列為主隊列,主隊列自動會放到主線程中執(zhí)行
- GCD還自帶一個全局的并發(fā)隊列,可以設(shè)置優(yōu)先級(dispatch_get_global_queue)
同步和異步
- 同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
- 異步:具備開啟新線程的能力
|
同步 |
異步 |
串行 |
沒有開啟線程,串行執(zhí)行任務(wù) |
開啟一條線程,串行執(zhí)行任務(wù) |
并發(fā) |
沒有開啟線程,串行執(zhí)行任務(wù) |
開啟線程(大于等于1條,不受控制),并發(fā)執(zhí)行任務(wù) |
各種函數(shù)隊列演示代碼
同步串行(打印結(jié)果12345)
//創(chuàng)建一個串行隊列
dispatch_queue_t queue = dispatch_queue_create("duilie", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建一個并行隊列
dispatch_queue_t queeu = dispatch_queue_create("bbb", DISPATCH_QUEUE_CONCURRENT);
//同步執(zhí)行這個串行隊列(順序執(zhí)行,不會創(chuàng)建新的線程)
dispatch_sync(queue, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
同步并行(打印結(jié)果12345)
//同步執(zhí)行一個并行隊列(順序執(zhí)行,沒有開啟線程,只是允許并行執(zhí)行,但是沒有線程這個條件,實際還是串行執(zhí)行)
dispatch_sync(queeu, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_sync(queeu, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
異步串行(打印結(jié)果1234,5不一定插在1234的哪個位置,因為1234和5是并行的,1234是串行的)
//異步執(zhí)行這個串行隊列(1234順序執(zhí)行,5和1234并行,開啟一條線程,)
dispatch_async(queue, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
異步并行(打印結(jié)果隨機,12345五個任務(wù)執(zhí)行都是并發(fā))
//異步執(zhí)行一個并行隊列(主線程和其他線程一起執(zhí)行,順序不一定,開啟的線程數(shù)也不一定)
dispatch_async(queeu, ^{
NSLog(@"1%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"2%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"3%@",[NSThread currentThread]);
});
dispatch_async(queeu, ^{
NSLog(@"4%@",[NSThread currentThread]);
});
NSLog(@"5%@",[NSThread currentThread]);
系統(tǒng)提供的五種隊列(1主隊列 + 4全局子隊列)
//1.當(dāng)前隊列的優(yōu)先級2.沒用
/**
* DISPATCH_QUEUE_PRIORITY_HIGH 2
* DISPATCH_QUEUE_PRIORITY_DEFAULT 0
* DISPATCH_QUEUE_PRIORITY_LOW (-2)
* DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
*/
//獲取到全局子隊列
dispatch_queue_t qq = dispatch_get_global_queue(0, 0);
//獲取主隊列
dispatch_queue_t q = dispatch_get_main_queue();
GCD的其他函數(shù)
//延時執(zhí)行
//參數(shù):幾秒后執(zhí)行(填的是2,代表兩秒后執(zhí)行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延遲執(zhí)行的代碼片段");
});
NSLog(@"我先走了");
- 只執(zhí)行一次(線程安全,只有創(chuàng)建單例用它,其他地方不要用)
//只執(zhí)行一次(線程安全的)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只走一次");
});
//重復(fù)執(zhí)行
//1.重復(fù)的次數(shù) 2.在什么隊列重復(fù) 3.當(dāng)前次數(shù)的索引
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t a) {
NSLog(@"%zu",a);
}) ;
- 線程組
- 本質(zhì)是先把任務(wù)放到隊列中,再把隊列放入組內(nèi)
- 優(yōu)點:可以監(jiān)聽組內(nèi)的任務(wù)完畢
//創(chuàng)建一個組(先把任務(wù)放進(jìn)隊列里,在放進(jìn)組里,組可以監(jiān)聽任務(wù)完畢)
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)1");
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)2");
});
//監(jiān)聽組內(nèi)執(zhí)行情況(監(jiān)聽組內(nèi)所有隊列執(zhí)行完畢)
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"執(zhí)行完畢");
});
- 單獨執(zhí)行(dispatch_barrier_async)
- 本質(zhì)是在并發(fā)隊列中擁有串行的權(quán)利
- 如果有12345個任務(wù),給3任務(wù)設(shè)置單獨執(zhí)行,按照12345的順序添加任務(wù),那么一定是12執(zhí)行完才會執(zhí)行3,3執(zhí)行完才會執(zhí)行45,所以12和45在執(zhí)行的時候是并發(fā),但是到了3,12和3和45其實是串行的
- 注意:如果想要這樣設(shè)置其執(zhí)行順序(其實是仿照NSOperation設(shè)置依賴),那么所在的隊列一定不能是系統(tǒng)提供的全局并發(fā)隊列
//創(chuàng)建并發(fā)隊列
dispatch_queue_t tt = dispatch_queue_create("tt", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(tt, ^{
NSLog(@"任務(wù)1");
});
dispatch_async(tt, ^{
NSLog(@"任務(wù)2");
});
//單獨執(zhí)行
dispatch_barrier_sync(tt, ^{
NSLog(@"任務(wù)3");
});
dispatch_async(tt, ^{
NSLog(@"任務(wù)4");
});
dispatch_async(tt, ^{
NSLog(@"任務(wù)5");
});
- block變成函數(shù)執(zhí)行
- 本質(zhì)是將block塊變成c語言的函數(shù),兩種方式是一樣的(dispatch_async和dispatch_async_f)
dispatch_async_f(dispatch_get_global_queue(0, 0), @"1", sum);
- void sum (void *a)
{
NSLog(@"%@",a);
}
資源搶奪
- 再實際開發(fā)過程中會將常出現(xiàn)賣票效應(yīng),其實就是多個線程訪問同一個實例變量,并改變實例變量的值,就會導(dǎo)致實例變量的值發(fā)生錯誤,
- 互斥鎖(@synchronized)有效的解決了這個問題
- 互斥鎖原理:使用了線程同步技術(shù),多條線程在同一條線上執(zhí)行,并且按順序執(zhí)行
- 將要訪問的實例變量套用在互斥鎖里面
int a = 1000;
//資源搶奪(對象)
//互斥鎖帶的參數(shù)一定要傳一個對象,一般情況傳self
@synchronized(self) {
//鎖的是代碼
a = a - 1;
}